一、本文尝试解释以下问题
1. Activity被启动之后放在哪个任务栈当中?与哪些因素有关?
2. Activity的四种启动模式对Activity的启动有哪些影响?
3. 在Activity中使用startActivityForResult(intent, REQUESTCODE);和onActivityResult()
是否与被启动的Activity的launchMode有关?如果有关,有什么关系?
二、Activity被启动之后放在哪个任务栈当中?与哪些因素有关?
1.基本论断:Activity启动之后对应的任务站与Activity的两个属性taskAffinity和allowTask-
Reparenting有关
2.原理
(1)任务栈的标识----任务栈名称的确定
任务栈的标识是由任务栈的名称来确定的,例如,在通常情况下用包名标识一个唯一任务栈,清单文件如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.musictest01"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.musictest01.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AActivity"></activity>
<activity android:name=".BActivity"></activity>
</application>
</manifest>
该文件当中<appliacation>和<activity>标签都没有设置taskAffinity属性,所以这个应用的默认任务栈名称就是包名"com.example.musictest01" , activity默认也会放入这个任务栈当中。
<application>当中的属性android:taskAffinity指定该应用程序默认的任务栈名称,用来标识一个唯一的任务栈,如果不设置,则默认为包名
<activity>当中的属性android:taskAffinity指定该activity的“宿主”任务栈名称
(2)情景分析:
第一步、定义宿主工程,其Manifest.xml文件如下:(由ADT创建,不做任何修改)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.master"
……>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="com.example.master.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
第二步、定义奴隶工程,Manifest.xml如下(根activity加android:taskAffinity="com.example
.master")
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.slave"
……>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.slave.MainActivity"
android:label="@string/app_name"
android:taskAffinity="com.example.master">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AActivity">
</activity>
<activity android:name=".BActivity">
</activity>
</application>
</manifest>
第三步、结果
当我们启动宿主工程,按home键将宿主任务栈放到后台,然后启动奴隶工程,神奇的事情发生了:奴隶工程的MainActivity并没有被加载,而是显示的是宿主工程的MainActivity
第四步、 同样道理,反过来执行,我们首先启动奴隶工程,MainActivity-->AActivity-->BActivity,按home键,回到桌面启动宿主工程,结果并没有加载宿主工程的MainActivity,而是显示的是奴隶工程的BActivity.
第五步、结论
android:taskAffinity="com.example.master" 此属性指定了该activity所在的任务栈名称,如果系统当中已经存在指定的任务栈,那么该activity启动的时候会重新寄宿到宿主当中(这是android的官方说明,但是在实际的实践中发现,启动该activity的时候仅仅是将指定的任务栈推到前台,显示该任务栈最顶端的那个原activity,本activity并没有被创建,换言之,该activity的生命周期的相关方法并没有被执行);
如果系统当中没有指定的任务栈,那么系统会创建名为com.example.master 的任务栈,并将该activity放入到创建的任务栈当中。
第六步、诡异的事情:按照android官方文档的说明,宿主的activity当中应该要设置android:allow-
TaskReparenting="true",但是实验表明,不设置该属性亦可!
3. 与启动模式相结合的情景
3.1 android:taskAffinity 与 FLAG_ACTIVITY_NEW_TASK结合
(1)基本论断
如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。
(2)测试。
我们首先写一个应用,它有两个Activity(Activity1和Activity2),AndroidManifest.xml如下:
<application
android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Activity1"
android:taskAffinity="com.example.task"
android:label="@string/app_name">
</activity>
<activity android:name=".Activity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Activity2的代码如下:
public class Activity2 extends Activity {
private static final String TAG = "Activity2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Intent intent = new Intent(this, Activity1.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
return super.onTouchEvent(event);
}
}
//将activity1的启动模式改为new task ,并设置android:taskAffinity="com.example.task"
然后,我们再写一个应用MyActivity,它包含一个Activity(MyActivity),AndroidManifest.xml如下:
<application
android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MyActivity"
android:taskAffinity="com.example.task"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
我们首先启动MyActivity,然后按Home键,返回到桌面,然后打开Activity2,点击Activity2,进入Activity1(他的启动模式是new_task)。然后按返回键。
我们发现,我们进入Activity的顺序为Activity2->Activity1,而返回时顺序为 Activity1->MyActivity。这就说明了一个问题,Activity1在启动时,重新宿主到了MyActivity所在的Task 中去了。
3.2 android:taskAffinity 与singleTask结合
直接给结论:
当一个应用程序加载一个singleTask模式的Activity时,首先该Activity会检查是否存在与它的taskAffinity相同的Task。
(1)、如果存在,那么检查是否实例化,如果已经实例化,那么销毁在该Activity以上的Activity并调用该Activity的onNewIntent()。如果没有实例化,那么该Activity实例化并入栈。
(2)、如果不存在,那么就重新创建Task,并入栈。
3.3 android:taskAffinity 与singleInstance结合
直接给出结论:
(1)、当一个应用程序加载一个singleInstance模式的Activity时,如果该Activity没有被实例化,那么就重新创建一个Task,并入栈,如果已经被实例化,那么就调用该Activity的onNewIntent;
(2)、singleInstance的Activity所在的Task不允许存在其他Activity,任何从该Activity加载的其它 Actiivty(假设为Activity2)都会被放入其它的Task中,如果存在与Activity2相同affinity的Task,则在该 Task内创建Activity2。如果不存在,则重新生成新的Task并入栈。
(3) 如果在奴隶应用application1当中定义两个activity,MainActivity和AActivity,将AActivity的启动模式设置为singleInstance,并设置其taskAffinity为com.example.master; 从MainActivity中启动AActivity,毫无疑问,此时会创建一个新的任务栈,名称是com.example.master,并将AActivity放置到栈中。然后将application1置于后台(按HOME键)。
打开宿主应用application2(默认的任务栈名称是com.example.master),此时惊奇地发现:此应用打开的是application1的AActivity,按后退键,退回到application2的MainActivity,由此可以看出,当声明为singleInstance的activity所在的任务栈被其他应用程序使用的时候,这个任务栈里面的原activity会丧失原来的singleInstance要求的一个activity在一个任务栈当中的属性,但是请注意:原来的这个activity始终是在栈顶的,后来加入的activity会都在这个activity的下面,但是还是按照栈的形式进出规则!
(4)将上面的过程反过来,如果首先启动application2,系统当中存在了com.example.master 任务栈,然后启动SingleInstanceActivity,虽然他指定了taskAffinity,但是仍然不能将自己寄宿到com.example.master任务栈当中!他会重新开一个任务栈将自己放进去!
三、Activity的四种启动模式对Activity的启动有哪些影响?
重点说一下 singleTask,直接给结论:
1. 设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity,等于它的属性值taskAffinity的任务栈是否存在存在;如果存在这样的任务栈,它就会在这个任务栈中启动,否则就会在新任务栈中启动。因此,如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。
2. 如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的堆栈顶端中(这个过程会调用该Activity的onNewIntent())。
四、在Activity中使用startActivityForResult(intent, REQUESTCODE);和onActivityResult()
是否与被启动的Activity的launchMode有关?如果有关,有什么关系?
1.通常情况下,当launchMode设置为standard或者singleTop的时候,程序的执行流程如下所示
2.如果launchMode设置为singleTask或者singleInstance的时候,程序就会在setResult之前调用
onActivityResult(),这样就得不到从activity2返回的数据,所以需要注意!!!