Activity的生命周期和启动模式
2017-08-15 22:58
204 查看
本文是《Android开发艺术探索》第1章学习笔记
Android官网解释也很全面
典型情况下的生命周期:在用户参与的情况下,Activity所经过的生命周期的改变。
异常情况的生命周期:指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期。
onRestart:表示Activity正在重新启动,当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为所导致的,比如用户按Home键切换桌面或者用户打开一个新的Activity,这是当前的Activity就会暂停,也就是onPause和onStop被执行了,接着用户又回到了这个Activity,就会出现这种情况。
onStart:表示Activity正在被启动,这是Activity已经可见了,但是还没有出现在前台,还无法和用户交互。
onResume:表示Activity已经可见了,并且出现在前台并开始活动,可以和用户交互了。
onPause:表示Activity正在停止,此时可以做一些存储数据,停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须执行完毕,新的Activity的onResume才会执行。
onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。
onDestroy:表示Activity即将被销毁。
图 1 Activity的生命周期
官网的示例代码:
备注:
第一次启动:回调如下:onCreate ->onStart ->onResume.
当用户打开新的Activity或者切换到桌面的时候,回掉如下:onPause->onStop.特殊情况如果新Activity采用了透明主题,那么当前Activity不会调用onStop.
当用户再次回到原Activity时,回调过程:onRestart->onStart->onResume.
当用户按返回键时,回调:onPause->onStop->onDestory.
整个生命周期来说,onCreate和onDestory是配对的,标志着Activity的销毁和创建,并且只可能有一次调用;从是否可见来说,onStart和onStop是配对的随着用户的操作或者设备的点亮和熄灭,这两个方法可能被调用多次;从Activity是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次.
必须执行完onPause后新的Activity才能Resume,onPause和onStop都不能执行耗时操作,尤其是onPause,这也意味着尽量在onStop中做操作,从而使得新Activity尽快显示出来并切换到前台.
1当系统认为该activity可能会被回收时,系统会调用onSaveInstance 方法(home键 打开新Activity)当一个Activity重新打开时会调用onRestoreInstanceState
2.当activity的系统配置发生改变后,如果它被重新启动,那么系统会通过onSaveInstanceState来保存数据,然后在onRestoreInstanceState方法中恢复数据。
-
当系统配置发生变化后,Activity会被销毁,其中onPause、onStop、onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。
这个方法是在onStope之前,它和onPause没有既定的时序关系。
当Activity 被重建以后,系统会调用onRestoreInstanceState,并把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此,我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来看,onRestoreInstanceState的调用时机在onStart之后。
在onSaveInstanceState和onRestoreInstanceState方法中,系统会自动帮我们做一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会为我们保存当前Activity的视图结构。如文本框输入的数据(xml中设置id属性)
源码TextView onInstance
onRestoreInstanceState 源码
验证数据存储和恢复的例子:
接收的位置可以有两个:onRestoreInstanceState和onCreate,两者的区别是onRestoreInstanceState一旦被调用,其参数一定是
有值的,而onCreate 如果正常启动的话,其参数Bundle
savedInstanceState为null,官方建议使用savedInstanceState来恢复数据。
可见但非前台Activity —比如Activity中弹出的一个对话框,导致Activity不可见但是位于前台无法和用户直接交互。
后台Activity —已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统配置发生改变后,Activity会被系统重新创建。通过给Activity指定configChanges属性添加oriention这个值,常用的有 local、orivation和keybordHidden这三选项。
当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和修复数据。如果一个进程没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作很不适合脱离四大组件而独立运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入service中从而保证进程有一定的优先级,这样就不会轻易的被系统杀死。
当使用ApplicationContext去启动standard模式的Activity的时候会报错。原因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是非Activity类型的Context(如Applicationcontext)并没有所谓的任务栈。解决办法为带启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这时候实际上是以singleTask模式启动的。
singleTop:栈顶复用模式
这种模式下,如果新的Activity位于栈顶,那么这个Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以取出当前的信息。注意:这个Activity的onCreate、onStart方法不会被调用,以为它没有发生改变。
singleTask:栈内复用模式。
这是一种单例模式,在这种模式下,只要Activity在一个栈中,那么多次启动该Activity都不会创建新的实例,和singleTop模式一样,系统也会调用onNewIntent.具体一点,当一个具有singleTask模式的Activity请求启动以后,比如Activity A,系统首先会寻找是否存在想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放到栈中。如果存在A所需的任务栈,这时候要看A是否在栈中有实例存在,如果有实例存在,那么系统会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就创建A的实例并把A压入栈中。举几个例子
比如目前任务栈s1中的情况为ABC,这个时候Activity D 以singleTask模式请求启动,其所需的任务栈为s2,由于任务栈s2和D的实例都不存在,所以系统会先创建任务栈s2,然后再创建D的实例并将其入栈到s2.
另外一种情况,假设D所需的任务栈为s1,其他情况如上面的例子1所示,那么由于s1已经存在,所以系统会直接创建D的实例并将其入栈到s1.
如果D所需的任务栈为s1,并且当前任务栈s1的情况为ADBC,根据栈内复用原则,此时D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,于是最终s1的情况为AD。
4 .singleInstance:单实例模式
这是一种加强的singleTask模式,它除了具有singleTask模式所具有的特性以外,还加强了一点,那就是具有此模式的Activity只能单独的位于一个任务栈中,换句话说,比如Activity A 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独立在这个任务栈中,由于栈内复用的特性,后续均不会创建新的Activity,除非这个独特的任务栈被销毁了。
TaskAffinity :任务相关性。
这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。
可以为每个Activity都单独指定TaskAffinity属性,这个属性不能和包名相同,不然就没有意义了。
TaskAffinity属性主要和singTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用户可以通过切换后台任务栈再次调到前台。
当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。
当TaskAffinity和allowTaskReparenting结合使用的时候,这种情况比较复杂,会产生特殊的效果。当一个应用A启动应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动的后,此Activity会直接从应用A的任务栈转义到应用B的任务栈中。比如两个应用A和B,A启动了B的一个Activity C,然后按home键回到桌面,然后再单击B的桌面图标,这时候并不是启动了B的主Activity,而是重新显示了已经被应用A启动的Activity C或者说,C从A的任务栈转义到了B的任务栈中。可以这么理解,由于A启动了C,这个时候C只能运行在A的任务栈中,但是C属于B应用,正常情况下,它的TaskAffinity肯定不可能和A的任务栈相同(因为包名不同)。所以,当B被启动后,B会创建自己的任务栈,这个时候系统发现C原本想要的任务栈已经被创建了,所以就把C从A的栈转移过来了。
设置启动模式的两种方式:
通过androidMenifest 为Activity指定启动模式
Intent 设置标志位来为Activity指定启动模式
两种方式的区别:优先级的话第2种高于第1种,当两种同时存在时,以第2种为准;指定范围上不同:第1种方式无法直接为Activity设定FLAG_ACTION_CLEAR_TOP标识,而第2种方式无法为Activity指定singleInstance模式。
FLAG_ACTIVITY_NEW_TASK
这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在xml中指定该启动模式效果相同
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在xml中指定该启动模式效果相同
FLAG_ACTIVITY_SINGLE_TOP
具有该标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起使用,
FLAGE_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标识的Activity不会出现在历史Activity的列表中。它等同于在xml中指定Activity的属性为android:excludeFromRencents=”true”
隐式调用不需要指定需要指定被启动组件的信息。
原则上不应该既是隐式又是显示调用,如果都指定的话以显式调用为主。
隐式调用需要Intent 能够匹配目标组件的intent-filter中设置的过滤信息。
1 .action的匹配规则
和过滤规则中的任何一个action相同即可匹配成功(区分大小写)。
2.category匹配规则
和过滤规则中的每一个都相同。
不设置category规则也可以匹配,系统在调用startActivity 或者 startActivityForResult 的时候会默认为Intent 加上“android.intent.category.DEFAULT”这个category.
data的匹配规则
data的匹配规则和action类似,需要玩完全匹配。
data的语法
scheme: URL模式 ,比如 http,file(当intent-filter 只有一个data时 可以不写)
host: URI 主机名,如果host未指定,那么整个URI中的其他参数也无效。
port:URI中的端口号,比如80,仅当URL中指定了scheme和host参数时port参数才有意义。
path:表示完整的路径信息
pathPattern:也表示完整的路径信息,但是它里边可以包含通配符 “”,“”表示0个或多个任意字符,需要注意的是,使用正则表达式时,“”表示成“\”,”\”要表示成“\\”
pathPrefix:表示路径的前缀信息
匹配例子
匹配的Intent
要为Intent指定完整的data,必须要调用setDataAndType 方法,不能先调用setData 再调用setType,因为这两个方法会彼此清除对方。
在action 和category,有一类特殊的action和category比较重要,它们是:
两者共同作用是用来标明这是一个入口Activity并且会出现在系统的应用列表中,少了任何一都没有实际意义,也无法出现在系统的应用列表中,二者缺一不可。例外,针对Service和BroadcastcastReceiver,PackageManager同样提供了类似的方法去获取成功匹配的组件信息。
Android官网解释也很全面
典型情况下的生命周期:在用户参与的情况下,Activity所经过的生命周期的改变。
异常情况的生命周期:指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建,异常情况下的生命周期。
典型情况下生命周期方法
onCreate:表示Activity正在被创建,这是生命周期的第一个方法,在这个方法中,我们可以做一些初始化工作,比如调用setContentView去加载界面布局资源、初始化Activity所需数据等。onRestart:表示Activity正在重新启动,当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为所导致的,比如用户按Home键切换桌面或者用户打开一个新的Activity,这是当前的Activity就会暂停,也就是onPause和onStop被执行了,接着用户又回到了这个Activity,就会出现这种情况。
onStart:表示Activity正在被启动,这是Activity已经可见了,但是还没有出现在前台,还无法和用户交互。
onResume:表示Activity已经可见了,并且出现在前台并开始活动,可以和用户交互了。
onPause:表示Activity正在停止,此时可以做一些存储数据,停止动画等工作,但是注意不能太耗时,因为这会影响到新Activity的显示,onPause必须执行完毕,新的Activity的onResume才会执行。
onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。
onDestroy:表示Activity即将被销毁。
图 1 Activity的生命周期
官网的示例代码:
public class ExampleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // The activity is being created. } @Override protected void onStart() { super.onStart(); // The activity is about to become visible. } @Override protected void onResume() { super.onResume(); // The activity has become visible (it is now "resumed"). } @Override protected void onPause() { super.onPause(); // Another activity is taking focus (this activity is about to be "paused"). } @Override protected void onStop() { super.onStop(); // The activity is no longer visible (it is now "stopped") } @Override protected void onDestroy() { super.onDestroy(); // The activity is about to be destroyed. } }
备注:
第一次启动:回调如下:onCreate ->onStart ->onResume.
当用户打开新的Activity或者切换到桌面的时候,回掉如下:onPause->onStop.特殊情况如果新Activity采用了透明主题,那么当前Activity不会调用onStop.
当用户再次回到原Activity时,回调过程:onRestart->onStart->onResume.
当用户按返回键时,回调:onPause->onStop->onDestory.
整个生命周期来说,onCreate和onDestory是配对的,标志着Activity的销毁和创建,并且只可能有一次调用;从是否可见来说,onStart和onStop是配对的随着用户的操作或者设备的点亮和熄灭,这两个方法可能被调用多次;从Activity是否在前台来说,onResume和onPause是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次.
必须执行完onPause后新的Activity才能Resume,onPause和onStop都不能执行耗时操作,尤其是onPause,这也意味着尽量在onStop中做操作,从而使得新Activity尽快显示出来并切换到前台.
异常情况下的生命周期分析
什么情况下会调用onSaveInstanceState1当系统认为该activity可能会被回收时,系统会调用onSaveInstance 方法(home键 打开新Activity)当一个Activity重新打开时会调用onRestoreInstanceState
2.当activity的系统配置发生改变后,如果它被重新启动,那么系统会通过onSaveInstanceState来保存数据,然后在onRestoreInstanceState方法中恢复数据。
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.e("MainActivity", "onSaveInstanceState"); outState.putString("testStr", "testStr"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String test = savedInstanceState.getString("testStr"); Log.e("savedInstanceState", "" + test); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.e("onConfigurationChanged", "" + newConfig.getLayoutDirection()); }
-
情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
当系统配置发生改变后,Activity就会被销毁并重新创建,生命周期如下图所示当系统配置发生变化后,Activity会被销毁,其中onPause、onStop、onDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。
这个方法是在onStope之前,它和onPause没有既定的时序关系。
当Activity 被重建以后,系统会调用onRestoreInstanceState,并把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此,我们可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来看,onRestoreInstanceState的调用时机在onStart之后。
在onSaveInstanceState和onRestoreInstanceState方法中,系统会自动帮我们做一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会为我们保存当前Activity的视图结构。如文本框输入的数据(xml中设置id属性)
源码TextView onInstance
@Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); // Save state if we are forced to boolean save = mFreezesText; int start = 0; int end = 0; if (mText != null) { start = getSelectionStart(); end = getSelectionEnd(); if (start >= 0 || end >= 0) { // Or save state if there is a selection save = true; } } if (save) { SavedState ss = new SavedState(superState); // XXX Should also save the current scroll position! ss.selStart = start; ss.selEnd = end; if (mText instanceof Spanned) { Spannable sp = new SpannableStringBuilder(mText); if (mEditor != null) { removeMisspelledSpans(sp); sp.removeSpan(mEditor.mSuggestionRangeSpan); } ss.text = sp; } else { ss.text = mText.toString(); } if (isFocused() && start >= 0 && end >= 0) { ss.frozenWithFocus = true; } ss.error = getError(); return ss; } return superState; }
onRestoreInstanceState 源码
@Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState)state; super.onRestoreInstanceState(ss.getSuperState()); // XXX restore buffer type too, as well as lots of other stuff if (ss.text != null) { setText(ss.text); } if (ss.selStart >= 0 && ss.selEnd >= 0) { if (mText instanceof Spannable) { int len = mText.length(); if (ss.selStart > len || ss.selEnd > len) { String restored = ""; if (ss.text != null) { restored = "(restored) "; } Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd + " out of range for " + restored + "text " + mText); } else { Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd); if (ss.frozenWithFocus) { createEditorIfNeeded(); mEditor.mFrozenWithFocus = true; } } } } if (ss.error != null) { final CharSequence error = ss.error; // Display the error later, after the first layout pass post(new Runnable() { public void run() { setError(error); } }); } }
验证数据存储和恢复的例子:
import android.R.integer; import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; public class MainActivity extends Activity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) { String test = savedInstanceState.getString("extra_test"); Log.d(TAG, "[onCreate]restore extra_test:" + test); } findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.ryg.charpter_1.c"); //intent.setClass(MainActivity.this, SecondActivity.class); intent.putExtra("time", System.currentTimeMillis()); intent.addCategory("com.ryg.category.c"); intent.setDataAndType(Uri.parse("file://abc"), "text/plain"); startActivity(intent); } }); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, "onNewIntent, time=" + intent.getLongExtra("time", 0)); } @Override protected void onStart() { Log.d(TAG, "onStart"); super.onStart(); } @Override protected void onResume() { Log.d(TAG, "onResume"); super.onStart(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.d(TAG, "onConfigurationChanged, newOrientation:" + newConfig.orientation); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "onSaveInstanceState"); outState.putString("extra_test", "test"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); //Log.d(TAG, "onRestoreInstanceState"); String test = savedInstanceState.getString("extra_test"); Log.d(TAG, "[onRestoreInstanceState]restore extra_test:" + test); } @Override protected void onPause() { Log.d(TAG, "onPause"); super.onPause(); } @Override protected void onStop() { Log.d(TAG, "onStop"); super.onStop(); } @Override protected void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); }
接收的位置可以有两个:onRestoreInstanceState和onCreate,两者的区别是onRestoreInstanceState一旦被调用,其参数一定是
有值的,而onCreate 如果正常启动的话,其参数Bundle
savedInstanceState为null,官方建议使用savedInstanceState来恢复数据。
情况2:资源内存不足导致优先级的Activity被杀死
前台Activity — 正在和用户交互的Activity,优先级最高。可见但非前台Activity —比如Activity中弹出的一个对话框,导致Activity不可见但是位于前台无法和用户直接交互。
后台Activity —已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统配置发生改变后,Activity会被系统重新创建。通过给Activity指定configChanges属性添加oriention这个值,常用的有 local、orivation和keybordHidden这三选项。
<activity android:name=".ConfigChangedTesting" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和修复数据。如果一个进程没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作很不适合脱离四大组件而独立运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入service中从而保证进程有一定的优先级,这样就不会轻易的被系统杀死。
Activity的启动模式
standard:标准模式,也是系统的默认模式。当使用ApplicationContext去启动standard模式的Activity的时候会报错。原因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是非Activity类型的Context(如Applicationcontext)并没有所谓的任务栈。解决办法为带启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这时候实际上是以singleTask模式启动的。
Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getApplicationContext().startActivity(intent);
singleTop:栈顶复用模式
这种模式下,如果新的Activity位于栈顶,那么这个Activity不会被重新创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以取出当前的信息。注意:这个Activity的onCreate、onStart方法不会被调用,以为它没有发生改变。
singleTask:栈内复用模式。
这是一种单例模式,在这种模式下,只要Activity在一个栈中,那么多次启动该Activity都不会创建新的实例,和singleTop模式一样,系统也会调用onNewIntent.具体一点,当一个具有singleTask模式的Activity请求启动以后,比如Activity A,系统首先会寻找是否存在想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放到栈中。如果存在A所需的任务栈,这时候要看A是否在栈中有实例存在,如果有实例存在,那么系统会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就创建A的实例并把A压入栈中。举几个例子
比如目前任务栈s1中的情况为ABC,这个时候Activity D 以singleTask模式请求启动,其所需的任务栈为s2,由于任务栈s2和D的实例都不存在,所以系统会先创建任务栈s2,然后再创建D的实例并将其入栈到s2.
另外一种情况,假设D所需的任务栈为s1,其他情况如上面的例子1所示,那么由于s1已经存在,所以系统会直接创建D的实例并将其入栈到s1.
如果D所需的任务栈为s1,并且当前任务栈s1的情况为ADBC,根据栈内复用原则,此时D不会重新创建,系统会把D切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,于是最终s1的情况为AD。
4 .singleInstance:单实例模式
这是一种加强的singleTask模式,它除了具有singleTask模式所具有的特性以外,还加强了一点,那就是具有此模式的Activity只能单独的位于一个任务栈中,换句话说,比如Activity A 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独立在这个任务栈中,由于栈内复用的特性,后续均不会创建新的Activity,除非这个独特的任务栈被销毁了。
TaskAffinity :任务相关性。
这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。
可以为每个Activity都单独指定TaskAffinity属性,这个属性不能和包名相同,不然就没有意义了。
TaskAffinity属性主要和singTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用户可以通过切换后台任务栈再次调到前台。
当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。
当TaskAffinity和allowTaskReparenting结合使用的时候,这种情况比较复杂,会产生特殊的效果。当一个应用A启动应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动的后,此Activity会直接从应用A的任务栈转义到应用B的任务栈中。比如两个应用A和B,A启动了B的一个Activity C,然后按home键回到桌面,然后再单击B的桌面图标,这时候并不是启动了B的主Activity,而是重新显示了已经被应用A启动的Activity C或者说,C从A的任务栈转义到了B的任务栈中。可以这么理解,由于A启动了C,这个时候C只能运行在A的任务栈中,但是C属于B应用,正常情况下,它的TaskAffinity肯定不可能和A的任务栈相同(因为包名不同)。所以,当B被启动后,B会创建自己的任务栈,这个时候系统发现C原本想要的任务栈已经被创建了,所以就把C从A的栈转移过来了。
设置启动模式的两种方式:
通过androidMenifest 为Activity指定启动模式
<activity android:name=".FirstActivity" android:label="This is FirstActivity" android:launchMode="standard" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Intent 设置标志位来为Activity指定启动模式
Intent intent = new Intent(FirstActivity.this, FirstActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("param1", "data1"); intent.putExtra("param2", "data2"); startActivity(intent);
两种方式的区别:优先级的话第2种高于第1种,当两种同时存在时,以第2种为准;指定范围上不同:第1种方式无法直接为Activity设定FLAG_ACTION_CLEAR_TOP标识,而第2种方式无法为Activity指定singleInstance模式。
Activity 的Flags
标记位的作用很多,有大标记为可以设定Activity的启动模式,比如 FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;有的可以影响Activity的运行状态,比如 FLAG_ACTIVITY_CLEAR_TOP和FLAGE_ACTIVITY_EXCLUDE_FROM_RECENTS等。FLAG_ACTIVITY_NEW_TASK
这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在xml中指定该启动模式效果相同
FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在xml中指定该启动模式效果相同
FLAG_ACTIVITY_SINGLE_TOP
具有该标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起使用,
FLAGE_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标识的Activity不会出现在历史Activity的列表中。它等同于在xml中指定Activity的属性为android:excludeFromRencents=”true”
IntentFilter的匹配规则
显示调用Activity需要指定被启动组件的信息。隐式调用不需要指定需要指定被启动组件的信息。
原则上不应该既是隐式又是显示调用,如果都指定的话以显式调用为主。
隐式调用需要Intent 能够匹配目标组件的intent-filter中设置的过滤信息。
1 .action的匹配规则
和过滤规则中的任何一个action相同即可匹配成功(区分大小写)。
2.category匹配规则
和过滤规则中的每一个都相同。
不设置category规则也可以匹配,系统在调用startActivity 或者 startActivityForResult 的时候会默认为Intent 加上“android.intent.category.DEFAULT”这个category.
data的匹配规则
data的匹配规则和action类似,需要玩完全匹配。
data的语法
<data android:mimeType="strign" android:scheme="strign" android:host="string" android:port="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" />
scheme: URL模式 ,比如 http,file(当intent-filter 只有一个data时 可以不写)
host: URI 主机名,如果host未指定,那么整个URI中的其他参数也无效。
port:URI中的端口号,比如80,仅当URL中指定了scheme和host参数时port参数才有意义。
path:表示完整的路径信息
pathPattern:也表示完整的路径信息,但是它里边可以包含通配符 “”,“”表示0个或多个任意字符,需要注意的是,使用正则表达式时,“”表示成“\”,”\”要表示成“\\”
pathPrefix:表示路径的前缀信息
匹配例子
<intent-filter> <data android:mimeType="text/plain" /> </intent-filter>
匹配的Intent
intent.setDataAndType(Uri.parse("file://abc"), "text/plain");
要为Intent指定完整的data,必须要调用setDataAndType 方法,不能先调用setData 再调用setType,因为这两个方法会彼此清除对方。
public Intent setData(Uri data) { mData = data; mType = null; return this; } ```
public Intent setType(String type) { mData = null; mType = type; return this; }
两种过滤规则例子:
<intent-filter> <action android:name="com.ryg.charpter_1.c" /> <action android:name="com.ryg.charpter_1.d" /> <category android:name="com.ryg.category.c" /> <category android:name="com.ryg.category.d" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" android:scheme="file" /> <data android:mimeType="video/mpeg" android:scheme="http"/> <data android:mimeType="audio/mpeg" android:scheme="http"/> </intent-filter>
可以匹配的Intent 1 <code> Intent intent = new Intent("com.ryg.charpter_1.c"); intent.putExtra("time", System.currentTimeMillis()); intent.addCategory("com.ryg.category.c"); intent.setDataAndType(Uri.parse("file://abc"), "text/plain"); startActivity(intent); 2 <code> Intent intent = new Intent("com.ryg.charpter_1.c"); intent.putExtra("time", System.currentTimeMillis()); intent.addCategory("com.ryg.category.c"); intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg"); startActivity(intent); 3 <code> Intent intent = new Intent("com.ryg.charpter_1.c"); intent.putExtra("time", System.currentTimeMillis()); intent.addCategory("com.ryg.category.c"); intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg"); startActivity(intent); 下边两种效果是相同的 <code> <intent-filter> <action android:name="com.wbf.charpter_1.c"/> <data android:mimeType="audio/mpeg" /> <data android:scheme="http"/> </intent-filter> <intent-filter>
<action android:name="com.wbf.charpter_1.c"/> <data android:mimeType="video/mpeg" android:scheme="http"/> </intent-filter>
> 判断Activity是否能够匹配我们的隐式Intent,判断是否能启动Intent。可以采用PackageManager 的两种方式 > 1.queryIntentActivities可以用来检查匹配,返回的是所有成功匹配的Activity > 2.resoloveActivity,如果没有找到就返回null。
Intent intent = new Intent("com.wbf.sss.c"); intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg"); PackageManager packageManager = getPackageManager(); List list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
在action 和category,有一类特殊的action和category比较重要,它们是:
<intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter>
两者共同作用是用来标明这是一个入口Activity并且会出现在系统的应用列表中,少了任何一都没有实际意义,也无法出现在系统的应用列表中,二者缺一不可。例外,针对Service和BroadcastcastReceiver,PackageManager同样提供了类似的方法去获取成功匹配的组件信息。
相关文章推荐
- Android 开发艺术与探究 第一章 Activity 的生命周期和启动模式
- 深入理解Activity——生命周期、启动模式、taskAffinity
- Android开发探索第一章 Activity生命周期及启动模式总结(三)
- activity4种启动模式,生命周期的理解
- Activity的生命周期和启动模式——IntentFilter的匹配规则
- Android学习笔记(1)--关于activity的生命周期和启动模式
- Activity的生命周期和启动模式详解
- Activity的生命周期和启动模式
- 基础总结篇:Activity生命周期 四种启动模式 以及TASK
- [Andorid开发艺术探索 读书笔记]Activity的生命周期和启动模式 (二)
- android 学习activity生命周期和启动模式
- [基础] 1. Activity的生命周期和启动模式
- Activity 生命周期及启动模式
- 【Android开发艺术探索读书笔记】 第一章 Activity的生命周期与启动模式
- 详解Activity的生命周期与启动模式
- 第一章、Activity 的生命周期和启动模式
- Android基础:Activity回顾(生命周期、启动模式)
- 第1章 Activity的生命周期和启动模式 1.3 IntentFilter的匹配规则
- Android03-Activity生命周期及启动模式
- 《Android开发艺术探索》读书笔记--part1 Activity的生命周期和启动模式