Activity
活动是什么
活动是一种可以包含用户界面的组件,主要用于和用户进行交互,一个应用程序中可以包含零个或多个活动,也是一个Context对象
活动的基本用法
1.创建活动
项目中的任何活动都应该重写Activity的onCreate()方法
最好每一个活动都能对应一个布局文件
//MainActivity
package com.kenny.myapplication;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//给当前活动加载一个布局
}
}
2.创建布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:id="@+id/button"
android:text="Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!--android:id : 定义当前元素的一个唯一标识符
android:text:控件显示的文字
android:layout_width:指定当前元素宽度
android:layout_height:指定当前元素高度
match_parent:当前元素宽度与父元素一致
wrap_content : 当前元素高度刚好能包含里面的内容-->
</LinearLayout>
3.在AndroidManifest文件中注册
活动的注册声明要放在标签中
所有的活动都要在AndroidManifest.xml中进行注册
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.kenny.activitytest"
xmlns:android="http://schemas.android.com/apk/res/android">
<!--android:icon: 指定应用图标
android:label: 指定应用标题栏的内容及应用程序显示的名称
android:theme: 指定应用主题
-->
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SecondActivity">
</activity>
<!--指定注册的活动-->
<!--.FirstActivity:缩写,全称:com.kenny.activitytest.FirstActivity-->
<activity android:name=".FirstActivity">
<!--配置主活动-->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
4.在活动中使用Toast
Toast : Android 系统提供的一种非常好的提醒方式
Toast.makeText(Context contex,"MESSAGE",Toast.LENGTH_SHORT).show;
用法解析:通过静态方法makeText()创建出一个Toast对象,后调用show()将Toast显示出来。makeToast()方法需要传入三个参数:第一个参数是Context,即上下文。第二个参数即Toast显示的文本内容。第三个参数即Toast显示时长,Toast.LENGTH_SHORT/Toast.LENGTH_LONG
Demo:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
//通过findViewById()获取在布局文件中定义的元素并得到其实例(返回的是一个View对象)
Button change = (Button) findViewById(R.id.change);
change.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(FirstActivity.this,"爱你",Toast.LENGTH_SHORT).show();
}
});
}
5.销毁活动
finish() //关闭当前活动
使用Intent在活动间跳转
Intent: Android程序中各组件之间进行交互的一种重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间交互数据。一般被用于启动活动,启动服务以及发送广播等场景。
大致可以分为两种:显示Intent和隐式Intent
1.显式Intent
Intent intent = new Intent(Context packageContext, Class<?> cls);
startActivity(intent);
//Context :提供一个启动活动的上下文,Class:指定想要启动的目标活动
//startActivity():启动活动,接收一个Intent参数
2.使用隐式Intent
隐式Intent并不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的action和category等信息,然后交由系统来分析这个Intent,并帮我们找出合适的活动(可以响应该隐式Intent的活动)启动
通过在标签下配置的内容,可以指定当前活动能够响应的action和category
<activity android:name=".ImplicitIntent">
<intent-filter>
<!--指明当前活动可以响应的action-->
<action android:name="com.example.activitytest.ACTION_START"/>
<!--android.intent.category.DEFAULT:默认category-->
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.MY_CATEGORY"/>
</intent-filter>
</activity>
==ps:==活动中只有 <action> 和<cateegory>中的内容同时能够匹配上Intent中指定的action和category师,该活动才能响应该Intent。每个Intent中只能指定一个action,但能指定多个category
ActivityDemo:
Intent intent = new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("android.intent.category.MY_CATEGORY")
startActivity(intent);
3.隐式Intent用法
3.1展示网页
Demo
private void OpenWebsite(){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent);
}
解析:首先指定了Intent的action是Intent.ACTION_VIEW,这是一个Android系统内置动作,其常量值为android.intent.action.VIEW.。然后通过Uri.parse()方法,将一个网址字符串解析成一个Uri对象,再调用Intent的setData方法将Uri对象传递过去。
另外我们还可以在标签中再配置一个标签,用于更精确地指定当前活动能够响应何种类型的数据,该标签主要可以配置以下内容:
- android:scheme: 用于指定数据的协议部分,如上Demo中的https部分
- android:host:用于指定数据的主机名部分,如上例中的www.baidu.com部分
- android.port:用于指定数据的端口部分,一般在主机名后
- android.path:用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
- android:mimeType:用于指定可以处理的数据类型,允许使用通配符的方式进行指定
ps:只有标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能响应该Intent
3.2向下一个活动传递数据
//传输数据
Intent intent = new Intent(MainActivity.this,DataActivity.class);
String data = "这是我想传输的数据";
intent.putExtra("键",data);
startActivity(intent);
ps:putExtra():将要传递的数据暂存在Intent中。该方法接收两个参数:第一个参数是键,用于后面从Intent中取值,第二个参数是传递的数据。
//接收数据
Intent intent = getIntent();
String data = intent.getStringExtra("键");
Toast.makeText(DataActivity.this,"传输的数据: "+data,Toast.LENGTH_SHORT).show();
ps:getIntent : 获取用于启动本活动的Intent;getStringExtra:获取传递的数据。如果传入的是整型数据,使用getIntExtra()
3.3 返回数据给上一个活动
//跳转下一个活动,启动活动
Intent intent = new Intent(MainActivity.this,DataActivity.class);
startActivityForResult(intent,1);
startActivityForResult():可用于启动活动,但该方法在活动销毁的时候能够返回一个结果给上一个活动。该方法接收两个参数:第一个参数为intent,第二个参数为请求码(唯一值),用于在回调中判断数据的来源。
//返回数据到上一个活动
Intent intent = new Intent();
intent.putExtra("data_return","Hello FirstActivity");
setResult(RESULT_OK,intent);
finish();
setResult:向上一个活动传输数据。该方法接收两个参数:第一个参数用于向上一个活动返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED。第二个参数则把带有数据的Intent传递回去
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch (requestCode){
case 1: //请求码
if(resultCode == RESULT_OK){
String returnData = data.getStringExtra("data_return");
Toast.makeText(MainActivity.this,"返回的数据:"+returnData,Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
解析:通过检查requestCode的值判断数据来源,确定数据是从活动返回的,我们再次通过resultCode的值判断处理结果是否成功,最后从data中取值并打印出来
ps:onActivityResult()方法带有三个参数,第一个参数requestCode,即我们在启动活动时传入的请求码。第二个参数resultCode,即我们在返回数据时传入的处理结果。第三个参数data,即携带着返回数据的Intent
活动的生命周期
1.返回栈
- Android中的活动是可以层叠的。没启动一个新活动,就会覆盖在原活动之上,点击Back键会销毁最上面的活动,下面的一个活动就会重新显示出来。
- Android是使用Task来管理活动,一个任务就是一组存放在栈里的活动的集合,这个栈被称作返回栈。栈式一种后进先出的数据结构,在默认情况下,每当我们启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户
返回栈工作示意图
2.活动状态
每个活动在其生命周期中最多可能有四种状态
- 01.运行状态
当一个活动位于返回栈的栈顶时,这时活动处于运行状态。 - 02.暂停状态
当一个活动不再处于栈顶位置,但依然可见时,活动就进入暂停状态。处于暂停状态的活动仍然是完全存活着的,只有在内存极低的情况下,系统才会去考虑回收这种活动(网页对话框) - 03.停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,活动就进入停止状态。系统仍然会为这种活动保存相应的状态和成员变量。当其他地方需要内存时,处于停止状态的活动有可能会被系统回收 - 04.销毁状态
当一个活动从返回栈中移除后就变成了销毁的状态。系统会最倾向于回收处于这种状态的活动,以保证手机内存充足。
3.活动的生存期
Activity类中定义了7个回调方法:
- onCreate():活动第一次被创建的时候被调用,应该在这个方法中完成活动的初始化操作。
- onStart():在活动由不可见变为可见的时候调用。
- onResume():在活动准备好和用户进行交互的时候调用,此时的活动一定位于返回栈的栈顶,并且处于运行状态。
- onPause():在系统准备去启动或恢复另一个活动的时候调用,通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop():在活动完全不可见的时候调用。它与onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行
- onDestroy():在活动被销毁之前调用,之后活动的状态将变为销毁状态。
- onRestart():在活动由停止状态变为运行状态之前调用,也就是活动被重新启动。
活动生存期
- 完整生存期:活动在 onCreate() 方法和 onDestroy() 方法之间所经历的,就是完整生存期,一般情况下,一个活动在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操作
- 可见生存期:活动在 onStart() 方法和 onStop() 方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用于总是可见的,即使有可能无法和用户进行交互。通过这两个方法,合理地管理那些对用户可见地资源。在onStart()方法中对资源进行加载,而在onStop()方法中对资源进行释放,从而保证处于停止状态地活动不会占用过多内存。
- 前台生存期:活动在 onResume() 方法和 onPause() 方法之间所处理地就是前台生存期。在其那天生存期内,活动总是处于运行状态,此时的活动时可以和用户进行交互的。
4.活动被回收
活动进入停止状态时,有可能被系统回收。
参照以下场景:应用中有一个活动A,用户在活动A的基础上启动了活动B,活动A就进入了停止状态,这个时候由于系统内存不足,将活动A回收掉了,然后用户按下Back键返回活动A,会出现什么情况呢?其实还是会正常显示活动A的,只不过这时并不会执行onRestart()方法,而是会执行活动A的onCreate()方法,因为活动A在这种情况下会被重新创建–次。
针对这个问题,Activity提供一个onSaveInstanceState()回调方法,该方法可以保证在活动被回收之前一定会被调用。该方法会携带一个Bundle类型的参数,Bundle提供一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正保存的内容。
在MainActivity中添加如下代码就可以将临时数据进行保存
@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
String tempData = "SomeThing you just typed";
outState.putString("data_key",tempData);
}
数据是已经保存下来了,那么我们应该在哪里进行恢复呢?我们一直使用的onCreate()方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null,但是如果在活动被系统回收之前有通过onSaveInstanceState()方法来保存数据的话,这个参数就会带有之前所保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。
修改MainActivity的onCreate()方法,如下所示:
@override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate( savedInstanceState);
Log.d(TAG,"onCreate");
setContentView( R.layout.activity_ main);
if (savedInstanceState != null) {
String tempData = savedInstanceState . getString("data_ key");
Log.d(TAG,tempData);
}
...
}
Intent还可以结合Bundle一起用于传递数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存
放在Intent里。 到了目标活动之后先从Intent中取出Bundle, 再从Bundle中一一 取出数据
活动启动模式
standard:在standard模式 (即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的个新的实例。
singleTop:当活动的启动模式指定为single Top,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)
Demo:
修改 AndroidManifest.xml中 MainActivity的启动模式,如下所示
<activity
android:name=".singleTopActivity"
android:launchMode="singleTop" />
singleTask:每次启动该活动时系统首先会在返回栈中检査是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
修改 Androidmanifest xm中MainActivity的启动模式,如下所示:
<activity
android:name=".singleTaskActivity"
android:launchMode="singleTask"/>
ps:在 SecondActivity中启动FirstActivity时,会发现返回栈中已经存在一个 FirstActivity的实例,并且是在 Secondactivityl的下面,于是 SecondActivity会从返回栈中出栈,而 FirstActivity重新成为了栈顶活动,因此 FirstActivityl的 onRestart()方法和 Secondactivity的 onDestroy()方法会得到执行.现在返回栈中应该只剩下一个 FirstActivity的实例了,按一下Back键就可以退出程序.
singleInstance:假设程序中有一个活动是允许其他程序调用的,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈。
(原因:每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例)
活动实践
结束当前活动
finish()
结束当前进程
android.os.Process.killProcess(androi.os.Process.myPid());
ps:killProcess()方法用于杀掉一个进程,它接收一个进程id参数。我们可以通过myPid()方法来获得当前程序的进程id。killProcess()方法只能用于杀掉当前程序的进程
自定义跳转活动
public void actionStart(Context context,Class activity,String data1,String data2){
Intent intent = new Intent(context,activity);
if(data1!=null&&data2!=null){
intent.putExtra("param1",data1);
intent.putExtra("param1",data1);
}
context.startActivity(intent);
}