文章参考 Android Activity 完全解析,Android【安卓】Activity详解。仅方便个人记忆。
一、正常情况下一个 Acitivity 会经历如下的生命周期:
- 生命周期介绍
1)onCreate
表示Activity正在被创建,做一些初始化动作,只在创建时调用一次。
2)onStart
表示Activity正在被启动,这时Activity已经可见,但不在前台,用户看不到也无法交互。
3)onResume
这里 Activity 已经可见了,可以和用户进行交互了,这时 Activity 处于栈顶,正在运行,这里要注意和 onStart 进行区分,可以理解为 onStart 和onResume 都表示 Activity 可见,但 onStart 的时候 Activity 还在后台,用户不可见,onResume 的时候在前台,用户可见
4)onPause
这个方法表示 Activity 正在停止,但还没有真正的停止,我们通常在这个方法中将一些消耗资源东西释放,可以存储一些关键的数据,但一定不能做耗时的操作,要不会意向下一个活动的使用
5)onStop
表示 Activity 即将停止,在 Activity 完全不可见得时候调用,可以做一些回收工作,也不能太耗时,这里要注意和 onPause 方法的区分,它和 onPause 方法的主要区别在于,如果新activity的方法是一个对话框式的活动,onPause 方法执行,onStop 方法不执行
6)onDestroy
表示 Activity 即将被销毁,对应于 onCreate 方法,这是 Acitivty 生命周期方法的最后一个回调方法,活动的状态将变为销毁状态
7)onRestart
这个方法在活动由停止状态变为运行状态时调用,这时候活动就是被重新启动了
- 生命周期介绍
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "MainActivity: onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "MainActivity: onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "MainActivity: onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "MainActivity: onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "MainActivity: onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "MainActivity: onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "MainActivity: onRestart");
}
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
}
这里我们创建了一个 MainActivity 并且重写了 Activity 生命周期的方法,这时候我们运行程序,在 Log 的控制台可以看到如下打印
MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
然后我们点击后退按钮,我们再来看一下控制台的打印内容
MainActivity: MainActivity: onPause
MainActivity: MainActivity: onStop
MainActivity: MainActivity: onDestroy
这时候一个 Activity 完整的生命周期就执行完毕了
接下来我们不点击后退键,我们点击 Home 键来看一下生命周期的情况
MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
MainActivity: MainActivity: onPause
MainActivity: MainActivity: onStop
这里要注意 Activity 并没有去执行 onDestroy,这里 Activity 只是变成了不可见状态,并没有被销毁,我们再点击打开应用
MainActivity: MainActivity: onRestart
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
这里注意到应用并没有调用 onCreate 重新创建,而是只调用了 onRestart,onStart,onResume 方法
这里我们就把在正常情况下的一个 Activity 的生命周期情况介绍完毕,总结如下:
- Activity的状态
每个activity在其生命周期中最多可能会有 4 种状态
1)运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态,系统最不愿意回收的就是出于运行状态的活动,因为这样会带来非常差的用户体验
2)暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态,你可能会觉得,既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏膜的,比如对话框形式的活动只会占用屏膜一部分的区域,处于暂停状态的活动任然是存活的,系统也不愿意去回收这种活动(因为它还是可见的),只有内存极低的情况下,系统才会去考虑回收这种活动
3)停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入停止状态,系统任然会为这种活动保存相应的状态和成员变量,但是当其他地方需要内存时,处于停止状态的活动有可能会被系统回收
4)销毁状态
当一个活动从返回栈中移除后就变成了销毁状态,系统会最倾向于回收处于这种状态的活动,从而保持手机的内存充足
二、多个 Activity 交互的生命周期:
这里我们新建一个 SecondActivity 类,我们在 MainActivity 中设置一个点击按钮,用来跳到 SecondActivity,并且和 MainActivity 一样复写生命周期的方法如下:
/**
* 生命周期测试SecondActivity
*/
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.i(TAG, "SecondActivity: onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "SecondActivity: onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "SecondActivity: onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "SecondActivity: onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "SecondActivity: onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "SecondActivity: onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "SecondActivity: onRestart");
}
}
现在我们来看分析生命周期的运行情况:首先打开 MainActivity,控制台打印生命周期运行如下:
MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
这时候我们再点击跳转按钮,控制台打印生命周期如下:
MainActivity: MainActivity: onPause
SecondActivity: SecondActivity: onCreate
SecondActivity: SecondActivity: onStart
SecondActivity: SecondActivity: onResume
MainActivity: MainActivity: onStop
这里我们思考如下内容
1)为什么要先调用 onPause 方法暂停当前显示的 MainActivity
2)为什么要等 SecondActivity 的 onCreate,onStart,onResume 方法执行完才执行 MainActivity的 onStop 方法,为什么不先执行当前 MainActivity的 onPause,onStop 方法呢?
在 MainActivity 打开 SecondActivity 的时候,会先执行 MainActivity 的 onPause 方法,然后执行 SecondActivity 的 onCreate、onStart、onResume 方法,完了再执行 MainActivity 的 onStop 方法,这里面还是有一种思想的,首先执行 MainActivity 的 onPause 方法是要关闭当前 MainActivity 的一些状态信息,如音频,视频等的状态,以免对新打开的 Activity 造成影响,试想我们正在听歌或看电影,这时候打进来一电话,如果我们不把当前的 Activity 先onPause 掉,会使一种怎样的体验,一边听歌,一遍打电话,不敢想象这样的场景,那么为什么又是在 MainActivity 执行完 onPause 而没有 onStop 的情况下就先执行 SecondActivity 的 onCreate,onStart,onResume 方法然后再执行 onStop 方法呢,这是因为考虑到如果在打开 SecondActivity 开启时发生 crash 的情况,这样如果 MainActivity 执行了 onStop 方法则会变为不可见状态,就会呈现出黑屏状态,用户什么都看不到。
三、横竖屏切换的时候 Acitivity 的生命周期:
- 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
- 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
- 设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
如果横竖屏切换的时候 Acitivity 的生命周期销毁掉然后会重新创建,所以我们展示的数据要在onSaveInstanceState 方法中去保存数据,在 MainActivity 中添加如下代码就可以降临时数据进行保存:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String data = "hello";
outState.putString("key",data);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String data = savedInstanceState.getString("key");
}
Log.i(TAG, "MainActivity: onCreate");
}
四、启动方式
Activity的启动分为显式和隐式启动,原则上二者不该同时存在,同时存在以显式为主。显式需要指定被启动对象的组件信息(包名、类名),隐式不需要指定目标组件信息,只要Intent可以匹配上目标Activity的IntentFilter设置的过滤信息。
- 1、使用显示Intent
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
private final int ACTIVITY_BACK = 1001;
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivityForResult(intent,ACTIVITY_BACK);
后面传递数据详解
- 2、使用隐式Intent
IntentFilter的过滤信息有action、category、data三种。一个Activity里可以有多组IntentFilter,每组IntentFilter里可以包含多个action、category、data,每组IntentFilter的任意三个及以上action、category、data形成一个匹配约束,只要Java代码里能够匹配一个匹配约束,即可启动目标Activity
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.weihy"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
1)action匹配规则
action是一个字符串,系统预定义了一些action,我们也可以在应用定义自己的action。Java代码里的Intent必须要有action,且这个字符串必须和xml中的action一致,区分大小写。
2)category匹配规则
category是一个字符串,系统预定义了一些category,我们也可以在应用定义自己的category。Java代码里category可以缺省,缺省时系统匹配xml中的android.intent.category.DEFAULT字符串,不缺省,匹配规则与action一致。
3)data匹配规则
如果xml里的IntentFilter定义了data,那么Java代码里的Intent必须要有data。
data由两部分组成mimeType和URI,
mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,
URI表示统一资源标识符,可以定位本地和网络的资源
//如下过滤规则
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
这种规则指定了媒体类型为所有类型的图片,Intent中mimeType必须为“image/*”才能匹配,xml中的过滤规则虽然没有指定URI但是scheme有默认值为content或file,所以intent中URI的scheme必须为content或file
//如下过滤规则
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*" android:scheme="file" android:host="asd"/>
</intent-filter>
intent.setDataAndType(Uri.parse("file://asd"),"image/*");
像这种指定了完整的data的xml,Java代码不可以先用setData方法,再用setType方法指定URI和mimeType,因为这两个方法会将对方的值互相清空,应该用setDataAndType方法。
使用隐式Intent不仅可以自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享称为可能。
启动浏览器:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri url = Uri.parse("https://www.baidu.com");
intent.setData(url);
startActivity(intent);
启动相册:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivity(intent);
启动电话界面:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri url = Uri.parse("tel:123456");
intent.setData(url);
startActivity(intent);
五、数据交互
说到了Intent那就不能不谈Activity之间的数据交互
1)简单的数据传递
//传递
Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
intent.putExtra("name","name");
intent.putExtra("age","age");
startActivity(intent);
//获取
Intent intent = getIntent();
String name =intent.getStringExtra("name");
String age = intent.getStringExtra("age");
2)传递 Bundle 对象
Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
//这里传递Bundle对象
Bundle bundle = new Bundle();
bundle.putString("name","姓名");
bundle.putString("age","0");
intent.putExtras(bundle);
startActivity(intent);
//获取方式同上
Intent intent = getIntent();
String name =intent.getStringExtra("name");
String age = intent.getStringExtra("age");
3)传递 JavaBean
当我们传递的数据比较大的时候,这时候我们可以直接传递一个 JavaBean,这里注意要实现 Serializable 接口
//Bean
public class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//传递
Person person = new Person("姓名", 0);
Intent intent = new Intent(MainActivity.this, ThirdActivity.class);
intent.putExtras("person", person);
startActivity(intent);
//获取
Intent intent = getIntent();
Person person = (Person) intent.getSerializableExtra("person");
4)返回数据给上个Activity
private final int ACTIVITY_BACK = 1001;
Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
startActivityForResult(intent,ACTIVITY_BACK);
//接收来自ThirdActivity的数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case ACTIVITY_BACK: //一个activity可能启动不同activity,用这个来区分
if(resultCode == RESULT_OK){ //这个字段区分栈顶activity是单纯的出栈,还是要向下传递消息
redBack = data.getIntExtra(RED_KEK,0);
greenBack = data.getIntExtra(GREEN_KEY,0);
blueBack = data.getIntExtra(BLUE_KEY,0);
}
break;
default:
break;
}
}
ThirdActivity的处理,这里我重写系统返回按键的onBackPressed方法,
因为你把setResult放在finish方法或者onDestroy方法里的话,退出得太快,消息可能没办法传递下去。
@Override
public void onBackPressed() {
getColorSendBack(selected);
super.onBackPressed();
}
//返回数据给上个activity
private void getColorSendBack(int mode){
if(redBack == NO_COLOR){
redBack = 0;
blueBack = 0;
greenBack = 0;
}
Intent intent = new Intent();
intent.putExtra(RED_KEK,redBack);
intent.putExtra(GREEN_KEY,greenBack);
intent.putExtra(BLUE_KEY,blueBack);
setResult(RESULT_OK,intent); //此处告诉上个activity,不是单纯结束,有消息下来了
}
六、启动模式
在说启动模式前,先看一下任务栈(返回栈)。
- Android使用任务(Task)来管理活动。一个任务就是一组存放在栈(也称返回栈 Back Stack)里的活动的集合。栈是一种先进后出的数据结构。
- 在默认情况下,每当我们启动一个新的活动,它会在任务栈中入栈,并处于栈顶的位置。每当我们销毁一个活动(按Back键或调用finish()方法),处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。
- 系统总是会显示处于栈顶的活动给用户。
-
默认情况下所有Activity所需的任务栈的名字为应用的包名。任务栈有前台和后台之分,后台栈中的Activity处于暂停状态,用户可以通过切换,将后台任务栈再次调到前台。
活动一共有四种启动模式:standard、singleTop、singleTask和singleInstance。
在AndroidManifest.xml中给< activity>标签指定android:launchMode属性来选择启动模式,如下:
<activity
android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- standard:标准模式 (默认)
每启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,并具有典型情况下的生命周期。谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。
使用ApplicationContext去启动standard模式的Activity会报错,非Activity类型的Context没有所谓的任务栈,要给它设立一个FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它建立一个新的任务栈,而不是去找原Context的任务栈。 - singleTop:栈顶复用模式
可以说是standard模式的“子类”(就Activity复用的条件和入的栈而言,是一个一般到特殊的过程)。
新的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建实例,因此它的onCreate、onStart方法不会被系统调用,而是会调用一个onNewIntent方法,通过这个方法,我们可以取得当前请求的信息。
如果新的Activity不在栈顶,即使已经存在,仍然是会重新创建Activity实例。 - singleTask:栈内复用模式
A:新的Activity要求的任务栈S存在。
检查栈S里是否存在新的Activity A,存在,因为clearTop效果,将A之上的Activity全部出栈,使其到达栈顶。
B:新的Activity要求的任务栈S不存在。
创建新的A的实例,并入栈S。
此外,这种模式也是和singeTop模式类似,会复用Activity实例,所以当重复创建时,不会调用onCreate、onStart方法,而是会调用onNewIntent方法。
可以说是singleTop模式的“子类” - singleInstance:单实例模式
可以说是singleTask模式的“子类”。它除了具有 singleTask 的全部属性外,那就是这种模式的 Activity 只能单独的位于一个任务栈中 - 给Activity指定启动模式的两种方法:
在AndroidManifest.xml中指定
android:launchMode=“singleTask”
在Intent中设置标志位
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
优先级代码里比xml高,要以代码里为准。
限定范围不同,xml里无法为Activity直接指定FLAG_ACTIVITY_CLEAR_TOP标识,代码里无法指定singleInstance模式。 - Activity的Flags
这里的标记位就是上文提到的使用Intent在代码里设置Activity启动方式的标记位。
常用标记位如下:
FLAG_ACTIVITY_NEW_TASK
为Activity指定启动模式为“singleTask”。
FLAG_ACTIVITY_SINGLE_TOP
为Activity指定启动模式为“singleTop”
FLAG_ACTIVITY_CLEAR_TOP
在“standard”模式下,使用这个标记位,如果默认任务栈里,已经有了目标ActivityA,那么A和A之上的Activity都要出栈,
然后新建一个A压入栈顶。与“singleTask”配合使用时,使用这个标记位,目标A之上的Activity都要出栈,并且onNewIntent方法会被调用。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标志的Activity不会出现在历史Activity的列表中,
当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。
等同在xml中指定Activity的属性为:
android:excludeFromRecents="true"
七、Activity管理
/**
* Activity 管理类
*/
public class ActivityManager {
//Activity存储实体
private static Stack<Activity> activityStack;
private static ActivityManager instance;
private ActivityManager() {}
/**
* 单一实例
*/
public static ActivityManager getInstance() {
if (instance == null) {
synchronized (ActivityManager.class) {
if (instance == null) {
instance = new ActivityManager();
}
}
}
return instance;
}
/**
* 添加 Activity 到堆栈
*/
public void addActivity(Activity activity) {
if (activityStack == null) {
activityStack = new Stack<Activity>();
}
activityStack.add(activity);
}
/**
* 从堆栈移除 Activity
*/
public void removeActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
}
}
/**
* 获取当前 Activity(堆栈中最后一个压入的)
*/
public Activity currentActivity() {
Activity activity = activityStack.lastElement();
return activity;
}
/**
* 结束当前 Activity(堆栈中最后一个压入的)
*/
public void finishActivity() {
Activity activity = activityStack.lastElement();
finishActivity(activity);
removeActivity(activity);
}
/**
* 结束指定的 Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
activity.finish();
}
}
/**
* 结束指定类名的 Activity
*/
public void finishActivity(Class<?> cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 结束所有 Activity
*/
public synchronized void finishAllActivity() {
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
activityStack.get(i).finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityMgr.restartPackage(context.getPackageName());
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
网友评论