启动模式相当于Activity的一个属性,不同的启动模式Activity会有不同的行为表现,这里的行为主要体现在Activity的生命周期,特别是系统在启动多个Activity实例的时候,具体差异我们将用实例来说明。
要了解Activity的启动模式,就不可避免地涉及到Activity所需的任务栈。什么是任务栈呢?android系统每启动一个新的Activity,都要将该Activity实例放入特定的任务栈,而这个任务栈和一个参数TaskAffinity有关,这个产生标识了Activity所需的任务栈的名字,默认的值为应用的包名。值得注意的是,TaskAffinity属性主要和singleTask启动模式(下面将要介绍)或allowTaskReparenting属性配合使用,在其他情况下没有意义。 既然叫“栈”,那么就符合“后进先出”的特点,我们每按一下back键,就有一个Activity实例出栈。
标准模式,这是系统的默认模式。每次启动一个Activity都会重新创建一个Activity实例,不管这个Activity的实例是否存在。
栈顶复用模式。在这种模式下,如果有Activity实例位于任务栈顶,那么就不会重新创建Activity实例,同时,Activity的onNewIntent方法会被回调。
栈内复用模式。类似于单例模式,只要任务栈中有一个此Activity的实例,那么重新启动Activity就不会创建新的实例,而且和singleTop一样,onNewIntent方法会被回调
单实例模式,这是一种加强的singleTask模式,只要有一个任务栈中有Activity的实例,那么新实例就不会被创建。换句话说,singleInstance的Activity实例只能位于一个任务栈中。
下面我将用运行实例代码结合日志输出来分析各种启动模式的区别
代码: LaunchMode应用:
package com.android.yanghuaan.launchmode; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; public class AActivity extends AppCompatActivity { private static final String TAG = "Activity_A"; @Override protected void onRestart() { Log.d(TAG, "restarted."); super.onRestart(); } @Override protected void onStart() { Log.d(TAG, "started."); super.onStart(); } @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "created."); super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); findViewById(R.id.start_B_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), BActivity.class); startActivity(intent); } }); findViewById(R.id.start_A_self_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), AActivity.class); startActivity(intent); } }); } @Override protected void onNewIntent(Intent intent) { Log.d(TAG, "new intent"); super.onNewIntent(intent); } @Override protected void onResume() { Log.d(TAG, "resumed."); super.onResume(); } @Override protected void onDestroy() { Log.d(TAG, "destroyed."); super.onDestroy(); } } package com.android.yanghuaan.launchmode; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; public class BActivity extends AppCompatActivity { private static final String TAG = "Activity_B"; @Override protected void onRestart() { Log.d(TAG, "restarted."); super.onRestart(); } @Override protected void onStart() { Log.d(TAG, "started."); super.onStart(); } @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "created."); super.onCreate(savedInstanceState); setContentView(R.layout.activity_b); findViewById(R.id.start_A_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), AActivity.class); startActivity(intent); } }); findViewById(R.id.start_B_self_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), BActivity.class); startActivity(intent); } }); } @Override protected void onResume() { Log.d(TAG, "resumed."); super.onResume(); } @Override protected void onDestroy() { Log.d(TAG, "destroyed."); super.onDestroy(); } } <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.yanghuaan.launchmode"> <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中改变启动模式,不同启动模式下此处代码不同--> <activity android:name=".AActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".BActivity"> </activity> </application> </manifest>LaunchMode应用包含两个Activity–A和B,每个Activity都有两个按钮,一个用来启动自己,另一个用来启动另一个Activity。 LaunchMode2代码:
package com.android.yanghuaan.launchmode2; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.start_other_activity_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClassName("com.android.yanghuaan.launchmode", "com.android.yanghuaan.launchmode.AActivity"); startActivity(intent); } }); } }LaunchMode2应用包含一个Activity,它可以用来启动LaunchMode应用的一个Activity。只有在singleInstance的例子中才使用到LaunchMode2应用,因此写好只需跑一次、将LaunchMode2安装到安卓手机上即可。
通过在Activity的生命周期方法中添加输出日志的代码来追踪每个Activity的活动
standard 模式
<activity android:name=".AActivity" android:launchMode="standard"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".BActivity"> </activity>操作步骤: 进入LaunchMode应用后点击两次启动自己的按钮 日志输出:
06-13 20:25:07.982 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:25:08.201 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:25:08.201 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:25:11.399 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:25:11.422 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:25:11.423 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:25:14.589 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:25:14.614 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:25:14.614 18980-18980/com.android.yanghuaan.launchmode D/Activity_A: resumed.分析: 从日志可以看出,onCreate->onStart->onResume被被调用了3次,说明创建了3个AActivity的实例,即Activity实例被重复创建了。此时需要3次按back才能返回桌面
singleTop 模式
<activity android:name=".AActivity" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".BActivity" android:launchMode="singleTop"> </activity>操作步骤: 进入LaunchMode应用后先按启动BActivity的按钮,此时进入了BActivity;再按启动AActivity的按钮,此时进入了AActivity;再按两次启动自己(AActivity)的按钮 日志输出:
06-13 20:35:07.446 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:35:07.483 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:35:07.483 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:35:10.828 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: created. 06-13 20:35:10.860 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: started. 06-13 20:35:10.860 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: resumed. 06-13 20:35:12.033 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:35:12.068 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:35:12.068 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:35:16.913 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: new intent 06-13 20:35:16.913 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:35:19.872 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: new intent 06-13 20:35:19.873 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: restarted. 06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: started. 06-13 20:35:24.084 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: resumed. 06-13 20:35:24.394 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: destroyed. 06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: restarted. 06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:35:24.951 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:35:25.252 4080-4080/com.android.yanghuaan.launchmode D/Activity_B: destroyed. 06-13 20:35:27.292 4080-4080/com.android.yanghuaan.launchmode D/Activity_A: destroyed.分析: 从AActivity到BActivity,再启动AActivity,发现onCreate被调用,即表明AActivity实例被重新创建;根据栈的特点可知此时AActivity的实例并没有位于栈顶,所以AActivity实例被重新创建; 返回AActivity后,再次启动两次AActivity都发现只有onResume和onNewIntent被调用,可知AActivity的实例被重新使用,并没有创建新的实例,这两次创建Activity的过程中AActivity的实例都位于栈顶,所以没有重新创建。 此时连续按back键的效果是:BActivity->AActivity->桌面;
singleTask 模式
<activity android:name=".AActivity" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".BActivity"> </activity>操作步骤: 进入LaunchMode应用,按启动BActivity的按钮启动BActivity,再按启动AActivity的按钮启动AActivity 日志输出:
06-13 21:21:09.731 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 21:21:09.760 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 21:21:09.760 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 21:21:12.204 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: restarted. 06-13 21:21:12.204 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: started. 06-13 21:21:12.204 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: resumed. 06-13 21:21:13.889 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: new intent 06-13 21:21:13.889 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: restarted. 06-13 21:21:13.890 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 21:21:13.890 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed.分析: 从BActivity启动AActivity,发现onRestart被调用,同时onNewIntent也被调用,表明AActivity的实例并没有被重新创建;此时只需按一次back键就可以返回桌面,因为在从BActivity创建AActivity的过程中BActivity被出栈了。
singleInstance 模式
<activity android:name=".AActivity" android:launchMode="singleInstance" android:allowTaskReparenting="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".BActivity"> </activity>操作步骤: 先启动应用LuanchMode,再按启动BActivity的按钮启动BActivity;然后通过 home键返回桌面,启动LaunchMode2应用,在里面按启动其他应用Activity的按钮启动AActivity;最后通过 home键返回桌面,启动LaunchMode应用 日志输出:
06-13 20:55:40.044 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: created. 06-13 20:55:40.136 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:55:40.137 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:55:41.046 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: created. 06-13 20:55:41.079 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: started. 06-13 20:55:41.079 21024-21024/com.android.yanghuaan.launchmode D/Activity_B: resumed. 06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: new intent 06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: restarted. 06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:55:49.998 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed. 06-13 20:56:00.336 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: new intent 06-13 20:56:00.336 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: restarted. 06-13 20:56:00.337 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: started. 06-13 20:56:00.337 21024-21024/com.android.yanghuaan.launchmode D/Activity_A: resumed.分析: 从LaunchMode2启动AActivity,发现AActivity并没有被重新创建,而是调用了onNewIntent和onRestart,并且重新从桌面进入LaunchMode应用也是如此,看起来就像AActivity从一个任务栈中移动到另一个任务栈中,这充分说明了singleInstance模式的Activity只能存在于一个任务栈中。