Activity的声明周期和启动模式
2016-10-04 15:22
260 查看
正常情况下Activity的声明周期
先来看看官方给出的声明周期图:这里需要说明如下几点:
针对特定Activity,第一次启动,回调如下:onCreate->onStart->onResume
当用户打开新的activity或者切换到桌面的时候,会回调onPause->onStop. 如果新的activity采用了透明的主题,那么当前activity不会回调onStop
当用户再次回到原activity时候,会回调如下:onRestart->onStart->onResume
当用户按下back键回退的时候,回调如下:onPause->onStop->onDestroy
当两个activity之间切换的时候,会先调用旧activity的onPause,然后新activity才启动。所以我们不能再onPause中做重量级操作,因为旧的activity的onPause先执行完成,新activity才能resume
异常情况下activity的生命周期
很多时候我们的activity可能会由于系统资源的紧缺被系统杀死,或者系统配置发生变化等系统配置发生改变导致activity被杀死并重新创建
如果当前activity从横屏直接旋转到竖屏,此时由于系统配置发生了变化,在默认情况下,activity就会被销毁并重新创建。当系统配置发生变化之后,activity会被销毁,其onPause,onStop,onDestroy均会被回调,同时由于activity是在异常情况下终止的,系统会回调onSaveInstanceState来保存当前activity的状态。onSaveInstanceState是在onStop之前调用的,onRestoreInstanceState是在onStart之后调用的
我们来看下面的栗子:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 这里当activity正常启动的时候,savedInstanceState是null,所以需要做非空判断 if (null != savedInstanceState) { String str = savedInstanceState.getString("killedStr"); Log.d(TAG,"onCreate runs...str is :"+str); } } /** * 当activity被销毁,并重新创建时候,回调该方法获取之前保存的数据 */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d(TAG,"onRestoreInstanceState runs..."); String str = savedInstanceState.getString("killedStr"); savedInstanceState.putString("killedStr","testString"); Log.d(TAG,"onRestoreInstanceState runs...str is :"+str); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG,"onSaveInstanceState runs..."); outState.putString("killedStr","testString"); } }
可以看到,我们可以同时在onRestoreInstanceState和onCreate中获取之前保存的数据,区别在于在onRestoreInstanceState方法中,其参数一定不是null,而在onCreate中,如果当前activity正常启动的时候,其参数为null
onSaveInstanceState方法,系统只会在Activity即将被销毁,并且有机会重新显示的情况下才会去调用它
资源内存不足导致activity被杀死
通常情况下我们的activity可以分为下面三种优先级:前台activity,表示正在和用户进行交互的activity
可见但非前台activity。
后台activity
到现在为止,我们知道了,当系统配置发生更改以后,activity会重新创建,我们可以给activity指定configChanges属性,来防止activity重新创建。常用的有locale,orientation,keyboardHidden.
需要注意到的是,在sdkversion > 13时候,为了防止旋转屏幕activity重启,除了orientation还需要添加screenSize
activity的启动模式
activity的LaunchMode
activity有四种启动模式:standard
标准模式,也是系统默认模式,每次启动一个activity都会重新创建一个新的实例,不管该实例是否已经存在,在该模式下,谁启动了和这个activity,那么这个activity就运行在启动它的那个activity所在的任务栈中。比如我们在service中启动标准模式的activity,就会遇到下面这种错误。
singleTop
在这种模式下,如果新activity已经位于任务栈的栈顶,那么此activity不会被重新创建,同时它的onNewIntent会被回调,该activity的onCreate,onStart,不会被系统调用。如果新的activity实例已经存在但不是位于栈顶,那么新的activity仍然会重新创建。
比如下面的activity启动过程:
singleTask
singleTask是一种単例模式,在该种模式下,只要activity在一个栈中存在,那么多次启动该activity都不会重新创建实例。系统也会调用其onNewIntent。
当一个具有singleTask模式的activity A请求启动之后,系统会先寻找是否存在A想要的任务栈,如果不存在,就会重新创建一个任务栈,然后创建A的实例,并把A放到栈中,如果存在A所需要的任务栈,这时候要看A是否在栈中有实例存在,如果有实例存在,那么系统会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就会创建A的实例,并把A放入栈中。
举下面栗子:
目前任务栈S1中的情况为ABC,此时Activity D以singleTask模式请求启动,其所需要的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,然后创建D的实例并将其放入S2中。
假设D所需要的任务栈是S1,其他情况和上面相同,由于S1已经存在,所以系统会直接创建D,并将其放入到栈S1
如果D所需要的任务栈为S1,并且当前任务栈S1的情况为ADBC根据栈内服用原则,此时D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop效果,会导致栈内所有D上面的activity全部出出栈,最小S1中的情况为AD.
- singleInstance
单实例模式,该模式除了具有singleTask模式的所有特性之外,另外需要注意的是,具有该种模式的activity只能够单独位于一个任务栈中。
TaskAffinity
默认情况下,所有的activity所需要的任务栈的名字为当前的包名,我们可以单独为每个activity指定TaskAffinity属性,TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting配对使用。当TaskAffinity和singleTask启动模式配对使用的时候,此时待启动的singleTask模式的activity所在的任务栈,和TaskAffinity是相同的。
当TaskAffinity和allowTaskReparenting结合使用的时候,当一个应用A启动了应用B的某一个activity,如果B的allowTaskReparenting属性为true,那么当应用B被启动以后,此activity会直接从应用A的任务栈转移到应用B的任务栈中。
为activity指定启动模式
我们可以分别在代码中或者AndroidManifest.xml里为activity指定启动模式。// 在AndroidManifest.xml中指定 android:launchMode="singleTask" // 在代码中指定,优先级较高 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
singleTask实栗
为MainActivity添加singleTask// 为MainActivity指定启动模式为singleTask <activity android:name="com.example.launchmode.MainActivity" android:launchMode="singleTask" android:label="@string/app_name" > </activity> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.id_text).setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Intent intent = new Intent(MainActivity.this,MainActivity.class); MainActivity.this.startActivity(intent); } }); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG,"onNewIntent runs...."); } @Override protected void onPause() { super.onPause(); Log.d(TAG,"onPause runs...."); } @Override protected void onResume() { super.onResume(); Log.d(TAG,"onResume runs...."); }
此时我们连续点击三次,启动当前MainActivity。
此时由于我们指定当前MainActivity的启动模式为singleTask,所以在默认的栈中,只有一个MainActivity实例,所以此时我们虽然启动了三次,但是按下back键盘的时候,会直接返回到桌面。
通过adb shell dumpsys activity 来查看当前的activity和其任务栈
为MainActivity去掉singleTask
现在我们为MainActivity去掉singleTask,做相同的操作。
Activity的Flags
FLAG_ACTIVITY_NEW_TASK这个标记位的作用是为Activity指定”singleTask”启动模式。
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定”singleTop”启动模式。
FLAG_ACTIVITY_CLEAR_TOP
当启动具有该标记为的activity,在同一个任务栈中,所有位于它上面的Activity都要出栈,该模式一般需要和 FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况被启动activity如果已经存在,那么系统就会调用它的onNewIntent,如果被启动的activity采用standard模式启动,那么连同他之上的activity都要出栈,系统会创建activity实例,并放入到栈顶。
IntentFilter的匹配规则
我们都知道,隐式启动一个activity时候,就需要匹配当前activity的IntentFilter设置,如果匹配不成功,将无法启动目的activity,IntentFilter中设置的过滤信息有action,category,data。为了匹配过滤列表,需要同时匹配action,category,data信息,否则会匹配失败,只有一个intent同时匹配action类别,category类别,data类别才算完全匹配,另外一个activity可能有多个intent-filter,一个intent只要能够匹配任何一组intent-filter即可以成功启动对应的activity
action的匹配规则
一个过滤规则中可以有多个action,只要intent中的action只要能够和其中的一个action相同即可匹配成功。action的匹配而规则要求intent中的action存在且必须和过滤规则中的一个action相同。category的匹配规则
我们的代码中可以没有category,但是XML中要加上”android.intent.category.DEFAULT”这句。如果你在代码中定义了一个或者多个category,那么你必须跟XML文件中定义的一样。比如你定义了一个category,那么要在XML文件中匹配到一个,,如果你定义了多个category,那么要在XML文件中全部匹配
data的匹配规则
data的匹配规则和action类似,它也要intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data,这里的完全匹配是指过滤规则中出现的data部分也出现在了Intent的data中。如果要为intent指定完整的data,必须要调用setDataAndType方法,因为setData和setType方法中,会彼此清除掉对方设置的值
对intent的匹配结果做判断
如果我们使用intent匹配了一个activity,并且会启动当前的activity,那么如果当前的activity不存在即匹配失败的话,就会出现ActivityNotFoundException这样的错误。我们可以采用PackageManager的resolveActivity或者intent的resolveActivity方法,如果找不到匹配的activity,就会返回null,我们通过返回值可以规避上面的错误。
另外PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity不同之处在于:它返回的不是最佳匹配的activity,而是所有成功匹配的activity。
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,int flags); public abstract ResolveInfo resolveActivity(Intent intent,int flags);
上面两个方法第二个参数,使用时候需要注意,我们需要使用MATCH_DEFAULT_ONLY这个标记为,该标记为的含义是仅仅匹配在intent-filter中声明了
<category android:name="android.intent.category.DEFAULT"/>
这样的activity。这样做的好处在于只要上面两个方法返回值不是null,则startActivity一定可以成功。
ok,今天就到这里了,本篇博客来自android开发艺术探索的学习笔记。
相关文章推荐
- 第1章 Activity的生命周期和启动模式 1.1 Activity的声明周期全面分析
- Android 组件Activity(一)之周期与启动模式
- Android初级——Activity状态、周期、启动模式
- Activity生命周期和和启动模式
- activity生命周期实例(不同启动模式)
- activity启动模式详解
- Activity的声明周期
- Activity的启动模式分析-之一
- Activity的启动模式分析-之二
- Activity的启动模式(android:launchMode)
- activity启动模式分析
- anandroid activity的启动模式 《转》
- Activity声明周期中各个事件的执行时机
- Activity的启动模式(android:launchMode)
- Android中的Activity四种启动模式(launchMode)
- Activity启动模式
- Activity启动模式1
- Android启动模式对activity行为的影响
- Activity启动模式singleTask,singleInstance研究
- android activity 的启动模式