Activity在Android APP中负责与用户的交互,是APP界面功能的载体和入口。一个APP中可以存在多个Activity,每个Activity就是一个交互界面。它们可以运行在一个进程中,也可以运行在不同的进程中。那么Android系统是怎么管理这些Activity的呢?Activity是怎么被启动的?它的生命周期又是怎样的呢?
目录
- Activity的管理-任务栈
- Activity的生命周期
2-1 正常情况下的生命周期
2-2 异常情况下的生命周期
2-3 Activity生命周期流程图 - Activity的启动模式
3-1 standard模式
3-2 singleTop模式
3-3 singleTask模式
3-4 singleInstance模式
3-5 启动模式与startActivityForResult
一、Activity的管理--任务栈
每个Activity的实例都是保存在任务栈中,这个任务栈是一个堆栈,满足先进后出的特点,由Android系统创建。因此,它的特性如下:
a、保存Activity实例
b、栈名由属性taskAffinity属性指定,默认情况下就是包名
c、不依附于APP,依附于系统。因此,它可以被多个进程共享
d、因为存在多个任务栈,所以位于栈顶的Activity实例不一定可见,但可见的Activity一定存在栈顶。
前面两点容易理解,可这第三点怎么来验证呢?我们可以写两个简单的demo,指定的栈名一样,然后两个demo分别打印出任务栈的taskId,如果相同,这说明任务栈是可以被多个进程共享的。
Demo的代码如下:
public class BaseActivity extends AppCompatActivity{
....
protected void dumpTaskAffinity(){
try {
ActivityInfo info = this.getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Log.i(this.getClass().getSimpleName(), "current activity hascode = " + this.hashCode() + " taskId = " + getTaskId() + " taskAffinity = "+info.taskAffinity + " taskHasCode = " + info.hashCode());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpTaskAffinity();
}
}
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest">
<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=".MainActivity"
android:taskAffinity="com.carol.practice.task1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
注意:android:taskAffinity指定的名字必须是xxx.xxx的形式,如果写成android:taskAffinity=“xxx”这样的形式,在安装时会提示错误:
taskAffinity取名错误时的安装提示.png
demo1的代码如下:
public class Demo1BaseActivity extends AppCompatActivity{
...
protected void dumpTaskAffinity(){
try {
ActivityInfo info = this.getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Log.i(this.getClass().getSimpleName(), "current activity hascode = " + this.hashCode() + " taskId = " + getTaskId() + " taskAffinity = "+info.taskAffinity + " taskHasCode = " + info.hashCode());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
public class Demo1MainActivity extends Demo1BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpTaskAffinity();
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest1">
<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=".Demo1MainActivity"
android:taskAffinity="com.carol.practice.task1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
可以看下两个demo几乎是一样的,只是Activity的名字不同罢了。这个时候我们分别运行,他们的输出结果如下:
demo的输出结果.png
demo1的输出结果.png
对比我们可以看出,两个APP的栈ID都是19,栈名也是一样的。这说明任务栈确实不是APP创建的而是由系统创建的,并且可以被多个APP共享。
通过对比,我们发现两个APP的Activity实例的hascode值也是一样的,这是不是说明他们用的是同一个实例呢?其实,hascode并不能用来表明实例的地址,两个相同的hascode值不能说明是同一个实例,但可以通过hascode值的不等来判断不是同一个实例。
二、Activity的生命周期
2-1 正常情况下的生命周期
一个Acitvity从创建到销毁的过程就是它的一次完整的生命周期。对应的生命周期函数依次是onCreate、onStart、onResume、onPause、onStop、onDestory,在一次生命周期中,Activity表现为四种状态,分别是运行状态、暂停状态、停止状态和销毁状态。那怎么来区分当前Activity当前处在哪个状态呢,这个就跟前面讲的任务栈有关系了。
2-1-1、运行状态
Activity实例位于栈顶并可见并能与用户交互,对应的生命周期函数是onResume。执行onStart时,Activity可见,但是还无法和用户进行交互,当执行完onResume函数之后才可以和用户进行交互。这种状态下的Activity优先级最高,异常情况下,系统最不愿意回收这种Activity。
2-1-2、暂停状态
Activity实例不在栈顶但是可见,对应的生命周期函数是onPause。一般情况下,我们在做Activity切换时这个状态时很短暂的,因为第二个Activity会马上覆盖掉之前的Activity,是之前的Activity不可见。但如果你要切换到的Activity不是占满屏幕类似对话框(不是Dialog)这样的Activity时就会处一直处在这个状态,直到发生下一次切换。从暂停状态回到运行状态会直接执行onResume函数。这种状态下的Activity优先级较高,异常情况下,如果存在更低优先级的Activity,系统也不会回收它。
2-1-3、停止状态
Activity实例不在栈顶且不可见,对应的生命周期函数是onStop。此时要切换到的Activity会完全覆盖掉之前的Activity。这种状态下的Activity系统仍然会保存它,只有在异常情况下,比如内存不够了,它的优先级最低,系统会回收它。
2-1-4、销毁状态
Activity实例被从任务栈中移除,对应的生命周期函数是onDestory。处于这种状态下,不管是否出现异常情况,系统都会回收。
在Activity的生命周期中还有两个函数onSaveInstanceState和onRestoreInstanceState。这个onSaveInstanceState函数,在正常启动Activity时不会被调用。它的调用时机有如下几个:
a、点击home键回到手机界面时。
b、从Activity A跳转到Activity B时,A的onSaveInstanceState方法会被调用。
c、发生异常,比如系统配置发生改变时,当前Activity被杀死时。
至于onRestoreInstanceState方法只会在发生异常,比如系统配置发生改变时,当前Activity被杀死后重新创建时调用,它的调用时机是在onStart之后,onResume之前。
2-2 异常情况下的生命周期
前面所讲是正常情况下Activity的生命周期流动情况,在异常情况下,当前的Activity或是整个进程都会被杀死,这个时候Android系统又提供了另一些处理函数,比如保存当前的数据防止数据丢失。异常情况主要分两种,资源相关的系统配置发生改变和系统内存不足。
2-2-1 资源相关的系统配置发生改变导致当前的Activity被杀死重建
首先,系统配置发生改变是一种什么样的情况?最常见的例子就是手机的横屏和竖屏之间的切换,这种情况下,系统配置会自动改变,此时当前的Activity会被杀死并重新创建。注意,要杀死的Activity是当前处于运行状态的Activity(不能说是栈顶的Activity,因为可能存在多个任务栈),处于其他状态下的Activity不会被杀死。被杀死时,系统会调用当前Activity的onPause、onStop、onDestory还有onSaveInstanceState方法。onPause、onStop、onDestory是依次调用的,至于onSaveInstanceState方法的调用可能在onPause之前也可能在onPause之后,但一定在onStop之前。这个怎么验证呢?可以创建两个Activity,第一个Activity启动第二个Activity,然后旋转屏幕。为了说明,杀死的是运行状态的Activity而不是只是存在栈顶的Activity,我们把启动的第二个Activity的启动模式指定为singleTask,任务栈名和第一个Activity的不同。代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest1">
<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=".Demo1MainActivity"
android:taskAffinity="com.carol.practice.task1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Demo1BaseActivity"/>
<activity android:name=".Demo1FirstActivity"
android:launchMode="singleTask"
android:taskAffinity="com.carol.practice.task2"/>
</application>
</manifest>
public class Demo1MainActivity extends Demo1BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpTaskAffinity();
Button btn = (Button) findViewById(R.id.btn_route);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Demo1MainActivity.this, Demo1FirstActivity.class);
startActivity(intent);
}
});
}
}
Demo1MainActivity的启动模式标准启动模式、任务栈名是"com.carol.practice.task1"
public class Demo1FirstActivity extends Demo1BaseActivity {
private static final String TAG = "Demo1FirstActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity_layout);
dumpTaskAffinity();
}
}
Demo1FirstActivity的启动模式singleTask,任务栈名是"com.carol.practice.task2"。和Demo1MainActivity不一样。
public class Demo1BaseActivity extends AppCompatActivity{
private static final String TAG = "Demo1BaseActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, getClass().getSimpleName() + " onCreate");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i(TAG, getClass().getSimpleName() + " onNewIntent");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, getClass().getSimpleName() + " onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + " onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + " onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, getClass().getSimpleName() + " onRestart");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, getClass().getSimpleName() + " onStop");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG, getClass().getSimpleName() + " onSaveInstanceState");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, getClass().getSimpleName() + " onRestoreInstanceState");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, getClass().getSimpleName() + " onDestroy");
}
protected void dumpTaskAffinity(){
try {
ActivityInfo info = this.getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Log.i(TAG, this.getClass().getSimpleName() + " current activity hascode = " + this.hashCode() + " taskId = " + getTaskId() + " taskAffinity = "+info.taskAffinity + " taskHasCode = " + info.hashCode());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
运行,从Demo1MainActivity跳转到Demo1FirstActivity,然后旋转。结果如下:
系统配置发生改变导致当前Activity被杀死后重现创建.png
从输出结果看,可以得出几点:
a、系统配置更改时要杀死只是当前运行状态的Activity,不是Activity处于栈顶就会被杀死。
b、杀死当前Activity之前onSaveInstanceState方法被调用。
c、当前Activity重新创建之后onRestoreInstanceState方法在onStart之后被调用。
那如果系统上同时运行两个APP呢?
结果就不贴了,隐藏在后台的进程它的Activity都处在停止状态,所以当发生屏幕旋转时,不会被杀死,只有当前处于运行状态的Activity会被杀死重建。
系统配置对应一个属性configChange,如果我们不想在屏幕发生旋转时杀死当前Activity可以设置这个属性,如下:
android:configChange="orientation|screenSize"
注意:只设置orientation是没有效果的,屏幕旋转仍会杀死重建,需要和screenSize组合在一起使用才行。
2-2-2 资源内存不足导致低优先级的Activity被杀死
Activity的优先级我们在前面讲Activity生命周期的四种状态时已经讲过了,优先级最低就是Activity处在停止状态的时候。当系统内存不足的时候,系统就会杀死这些最低优先级的Activity,在《Android开发艺术探索》中说是会杀掉拥有最低优先级Activity的进程,因此,这里不太明白到底只是杀死低优先级的Activity还是整个进程被杀死。不过根据Android官方开发文档的流程图来看,当内存不足时,处在暂停状态和停止状态的Activity所在的进程会被杀死,进程被杀死之前还是会调用onSaveInstanceState,当要返回到这个Activity时会重新创建进程并重新创建这个Activity。官方文档的Activity的生命周期流程图如下:
Activity的生命周期官方文档.png
2-3 Activity生命周期流程图
前面说了那么多,还是来一个自己总结的流程图吧。
Activity生命周期流程图.png
这里说明一下,前面说onSaveInstanceState和onPause谁先执行谁后执行不能确定,但根据我在Android5.1系统上测试情况来看都是在onPause先调用onSaveInstanceState后调用。
三、Activity的启动模式
Activity的启动模式有四种:standard模式、singleTop模式、singleTask模式和singleInstance模式。这四种模式对应四种对Activity实例不同的管理方式。
给Activity指定启动模式有两种办法:通过Intent的FLAG_ACTIVITY_XXX标志来指定启动模式。如下:
Intent intent = new Intent();
intent.setClass(MainActivity.this, FirstActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
另一种就是在AndroidManifest.xml中通过launchMode属性指定启动模式。比如:
<activity android:name=".FirstActivity"
android:launchMode="singleTask"
android:taskAffinity="com.carol.practice.task1"/>
3-1 standard模式
标准模式,系统默认的启动方式。它的特点如下:
a、每启动一个Activity实例就会创建一个Activity实例入栈。不管当前任务栈中是否已经存在这个实例了。
b、谁启动了这个Activity,那么这个Activity实例会进入启动它的Activity所在的任务栈中。比如,Activity A启动Activity B,A指定的任务栈是a,B是标准模式,指定的任务栈是b,此时B的实例进入的任务栈a而不是指定任务栈b。这说明,这种模式下taskAffinity是无效的。虽然b对应输出的taskAffinity就是它指定的名字但是任务栈的ID还是A对应的任务栈的。这里也说明了任务栈另一个问题,比较任务栈是否相同只看它的taskId是否相同,不看taskAffinity。
c、由于在标准模式具有b特性,因此如果使用非Activity类型的Context(如果ApplicationContext)启动标准模式的Activity将会报错。因为非Activity类型的Context没有任务栈。如图所示:
非Activity类型的Context启动标准模式的Activity.png
从错误提示中,可以看出标准模式下Activity的c特点。解决这种错误的方法就是使用Intent的Flag标志,设置成FLAG_ACTIVITY_NEW_TASK,这样启动的时候就会为它创建一个新的任务栈。实际上是以singleTask的模式启动的。
3-2 singleTop模式
栈顶复用模式。它的特点如下:
a、跟标准模式一样,谁启动这种Activity它就进入谁的任务栈中,不受taskAffinity影响。
b、启动这种模式的Activity会先检查启动它的Activity所在任务栈的栈顶是否存在相同Activity,如果存在,则不重新创建,直接调用待启动Activity的onNewIntent的方法。如果栈顶不存在,则重新创建,不管该任务栈的栈中是否有这个Activity实例。
使用代码验证一下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest">
<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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".BaseActivity"/>
<activity android:name=".FirstActivity"
android:launchMode="singleTop"
android:taskAffinity="com.carol.practice.task1"/>
</application>
</manifest>
MainActivity是标准模式,FirstActivity是singleTop模式,并且制定任务栈名是"com.carol.practice.task1。
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpTaskAffinity();
Button btn = (Button) findViewById(R.id.btn_route);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivity(intent);
}
});
}
}
在MainActivity中点击按钮进入FirstActivity。
public class FirstActivity extends BaseActivity{
private static final String TAG = "FirstActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity_layout);
dumpTaskAffinity();
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
startActivity(intent);
}
});
}
}
FirstActivity中也有一个按钮,单击之后,自己启动自己。
其输出结果如下:
singleTop模式输出.png
从结果可以看出,第一、FirstActivity进入的是MainActivity所在的任务栈;第二、FirstActivity位于栈顶,在自己启动自己时FirstActivity先进入暂停状态然后依次调用onNewIntent和onResume并没有调用onCreate、onStart方法,说明没有被重新创建
3-3 singleTask模式
栈内复用模式。它的特点如下:
a、受taskAffinity影响。它只会进入taskAffinity指定的任务栈中。
b、指定的任务栈不存在,则先创建任务栈然后将Activity实例入栈;指定的任务栈存在,则检查栈内是否存在Activity实例,如果存在,则将位于该Activity实例之上的其他所有Activity实例出栈,将自己置于栈顶的位置。如果栈内不存在,则创建Activity实例。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest">
<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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".BaseActivity"/>
<activity android:name=".FirstActivity"
android:launchMode="singleTask"
android:taskAffinity="com.carol.practice.task1"/>
</application>
</manifest>
将FirstActivity的启动模式改为singleTask模式,taskAffinity指定为"com.carol.practice.task1",使其和MainActivity的taskAffinity不一致。
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpTaskAffinity();
Button btn = (Button) findViewById(R.id.btn_route);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivity(intent);
}
});
}
}
从MainActivity中切换到FirstActivity
public class FirstActivity extends BaseActivity{
private static final String TAG = "FirstActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity_layout);
dumpTaskAffinity();
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, MainActivity.class);
startActivity(intent);
}
});
}
}
从FirstActivity中切换到MainActivity。
输出结果如下:
singleTask模式输出.png
第一,MainActivity启动FirstActivity,FirstActivity实例进入的任务栈是FirstActivity指定的任务栈不是MainActivity的任务栈;第二,从FirstActivity启动MainActivity,MainActivity重新创建实例并进入FirstActivity所在的任务栈,因为MainActivity是标准启动模式;第三,再次重MainActivity启动FirstActivity时位于FirstActivit实例所在的任务栈栈顶的MainActivity先进入暂停状态然后依次调用FirstActivity的onNewIntent,onRestart、onStart和onResume,最后MainActivity被销毁。我们在singleTop模式下发现只是调用onNewIntent和onResume,为什么在singleTask模式还调用了onRestart和onStart呢?这跟Activity实例所处的状态有关,singleTop模式下,Activity实例已经在栈顶了,而现在的Activity实例是在停止状态,根据生命周期流程图从停止状态回到运行状态时需要一次调用onRestart、onStart和onResume的。
3-4 singleInstance模式
单例模式。在介绍它的特点之前,我们先来看两个例子(Activity A标准模式 , Activity B singleInstance模式。在Android5.1上测试):
情况1:APP启动时先启动A,创建任务栈A,然后A启动B,B单独创建一个任务栈B。再从B启动A,A重新创建实例进入任务栈A。由于singleInstance的独占任务栈的特性,A无法进入任务栈B,系统让其直接进入之前的任务栈A并重新创建A实例。每次从B启动A都是这样复用任务栈A重新创建A实例。这个可以理解。
情况2:APP启动时先启动B,创建任务栈B,然后B启动A,A单独创建一个任务A。再从A启动B,B复用之前的实例,接着B启动A,根据情况1应该是复用任务栈A重新创建A实例,可现在输出结果是复用任务栈A也复用A实例,不重新创建A实例。这是为什么呢?情况2这种,A都不符合标准模式的特性了(每次启动都会创建实例)。贴上我观察到的输出结果:
singleInstance模式输出结果.png
从输出结果可以看到,MainActivity第一创建完成之后,后面启动都不再重新创建了。造成这种差异性就是APP运行时第一次启动的Activity是标准模式还是singleInstance模式。如果第一次启动的Activity是标准模式,那么标准模式Activity和singleInstance模式的Activity之间的切换,标准模式的Activity会复用它所在的任务栈并重新创建实例;如果第一次启动的Activity是singleInstance模式,这个又要分情况。
在情况2中,A占用一个任务栈A,B占用一个任务栈B, B启动A,如果任务栈A只有A,不论多少个,A复用任务栈A也复用实例A,不再重新创建A实例;如果任务栈A不止有A实例还有C实例D实例,那么B启动A时都会重新创建A实例。这里就不再帖代码了。只对singleInstance模式的特点总结如下:
a、singleInstance模式下的Activity独占一个任务栈,全局唯一。由于任务栈中只有一个Activity实例,因此,可见并获得焦点时它是栈顶运行状态;可见但失去焦点时它是栈低暂停状态;不可见时它是栈低停止状态。
b、APP启动时,第一次启动的是standard模式下的Activity A,创建任务栈A,后面从A启动singleInstance模式的Activity B,因为a特点不论B是否重新指定taskAffinity,都会重新创建一个任务栈B。然后每次从B启动A时,A都会重新创建实例进入任务栈A。
c、APP启动时,第一次启动的是singleInstance模式的Activity B,创建任务栈B。然后从B启动standard模式的Activity A,如果A不存在会单独创建一个任务栈A,如果存在,则看任务栈A中是只有A实例(不论多少个)还是除A实例之外还有别实例。只有A实例则直接复用不重新创建;除A实例之外还有的别实例则每次都重新创建A实例。这种违反standard模式特性的情况,只在这里适用。
3-5 启动模式与startActivityForResult
前面介绍四种启动模式时我们在代码中要从一个Activity A跳转到另一个Activity B都是调用方法startActivity,如果我们想从B返回到A时把B中数据也传递给A则在A中调用方法startActivityForResult启动B(startActivityForResult的第二个参数requestCode必须是大于-1的数,不然不会回调onActivityResult),这样返回时Activity A的onActivityResult方法就会被调用,该方法会带回B使用setResult方法给A的intent对象。代码如下:
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn_route);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivityForResult(intent, 0);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i(TAG, "requestCode=" + requestCode + " resultCode=" + resultCode);
if(resultCode == 100){
String str = data.getStringExtra("result");
Log.i(TAG, "FirstActivity delivered result : " + str);
}
}
}
public class FirstActivity extends BaseActivity{
private static final String TAG = "FirstActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity_layout);
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("result", "i am FirstActivity");
setResult(100, intent);
finish();
}
});
}
}
注意这里是B返回A,比如B自己finish或按back键,不是B启动A。B启动A是不会调用A的onActivityResult方法的。
那使用startActivityForResult启动Activity,在不同的启动模式会跟startActivity启动Activity有什么不同吗?我们通过代码来验证下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.carol.practice.activitytest">
<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=".LaunchModeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".StandardActivity"/>
<activity android:name=".SingleTopActivity"
android:launchMode="singleTop"/>
<activity android:name=".SingleTaskActivity"
android:launchMode="singleTask"
android:taskAffinity="com.carol.practice.task1"/>
<activity android:name=".SingleInstanceActivity"
android:launchMode="singleInstance"
android:taskAffinity="com.carol.practice.task2"/>
</application>
</manifest>
public class LaunchModeActivity extends BaseActivity implements View.OnClickListener{
private static final String TAG = "LaunchModeActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.launchmode_activity_layout);
Button standardBtn = (Button)findViewById(R.id.btn_standard_activity);
standardBtn.setOnClickListener(this);
Button singleTopBtn = (Button)findViewById(R.id.btn_singleTop_activity);
singleTopBtn.setOnClickListener(this);
Button singleTaskBtn = (Button)findViewById(R.id.btn_singleTask_activity);
singleTaskBtn.setOnClickListener(this);
Button singleInstanceBtn = (Button)findViewById(R.id.btn_singleInstance_activity);
singleInstanceBtn.setOnClickListener(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i("BaseActivity", "requestCode=" + requestCode + " resultCode=" + resultCode);
if(resultCode >= 100) {
String str = data.getStringExtra("result");
Log.i("BaseActivity", "delivered result : " + str);
}
}
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btn_standard_activity:
Intent standardIntent = new Intent(LaunchModeActivity.this, StandardActivity.class);
startActivityForResult(standardIntent, 100);
break;
case R.id.btn_singleTop_activity:
Intent singleTopIntent = new Intent(LaunchModeActivity.this, SingleTopActivity.class);
startActivityForResult(singleTopIntent, 101);
break;
case R.id.btn_singleTask_activity:
Intent singleTaskIntent = new Intent(LaunchModeActivity.this, SingleTaskActivity.class);
startActivityForResult(singleTaskIntent, 102);
break;
case R.id.btn_singleInstance_activity:
Intent singleInstanceIntent = new Intent(LaunchModeActivity.this, SingleInstanceActivity.class);
startActivityForResult(singleInstanceIntent, 103);
break;
default:
break;
}
}
}
public class StandardActivity extends BaseActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.standard_activity_layout);
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(StandardActivity.this, LaunchModeActivity.class);
startActivityForResult(intent, 100);
}
});
}
}
public class SingleTopActivity extends BaseActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.singletop_activity_layout);
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTopActivity.this, SingleTopActivity.class);
startActivityForResult(intent, 101);
}
});
}
}
public class SingleTaskActivity extends BaseActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.singletask_activity_layout);
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleTaskActivity.this, LaunchModeActivity.class);
startActivityForResult(intent, 102);
}
});
}
}
public class SingleInstanceActivity extends BaseActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.singleinstance_activity_layout);
Button btn = (Button)findViewById(R.id.btn_back);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SingleInstanceActivity.this, LaunchModeActivity.class);
startActivityForResult(intent, 103);
}
});
}
}
startActivityForResult和standard模式的Activity的输出结果:
startActivityForResult与standard模式的输出结果.png
从输出结果看,startActivityForResult与startActivity启动standard模式的Activity是一样的,符合standard模式的特点。
startActivityForResult和singleTop模式的Activity的输出结果:
startActivityForResult和singleTop模式的输出结果.png
从这个输出结果我们发现,startActivityForResult与startActivity启动singleTop模式的Activity有所不同了,当SingleTopActivity实例已经位于栈顶时,SingleTopActivity使用startActivityForResult自己启动自己还是会重新创建实例。如果使用startActivity自己启动自己就不会重新创建实例。
startActivityForResult和singleTask模式的Activity的输出结果:
startActivityForResult和singleTask模式的输出结果.png
从结果来看,跟standard模式的输出一样,但是我是指定了SingleTaskActivity的启动模式和taskAffinity的呀。系统并没有为SingleTaskActivity重新创建任务栈,还是使用的LaunchModeActivity的任务栈,而且最后一步从LaunchModeActivity中启动SingleTaskActivity时也是重新创建了实例,没有复用任务栈里的实例。
startActivityForResult和singleInstance模式的Activity的输出结果:
startActivityForResult和singleInstance模式的输出结果.png
这个和singleTask模式的输出结果是一致的,也是没有重新创建任务栈和复用任务栈中已经存在的SingleInstanceActivity实例。
因此,从这四种输出结果可以得出结论:
使用startActivityForResult(参数requestCode大于-1)启动Activity时会忽略launchMode和taskAffinity的作用,改为standard启动模式。launchMode和taskAffinity只对startActivity有效。
以上测试都是基于Android5.1系统。
网友评论