Activity LaunchMode & TaskAffinity
2012-10-25 16:46
351 查看
设计两个Activity,分别称为A,B,每个Activity上面都放置两个按钮,一个启动A,一个启动B。
界面如下:(上面的一行文字用来识别是在A界面还是在B界面)
在代码中实现onCreate():
Java代码
Button btnA = (Button)findViewById(R.id.btnA);
btnA.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(A.this, A.class));
}
});
Button btnB = (Button)findViewById(R.id.btnB);
btnB.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(A.this, B.class));
}
});
Log.e("Task:"+getTaskId(), "A" + mId + " created.");
onNewIntent():
Java代码
Log.e("Task:"+getTaskId(), "A" + mId + " onNewIntent.");
onDestroy():
Java代码
Log.e("Task:"+getTaskId(), "A" + mId + " destroyed.");
这样就可以直观的从Logcat输出中看到Activity的创建、销毁以及请求的响应。
1.两个Actiity的LaunchMode都为默认的standard,启动Ap,看到输出:
Task:9(380): A1 created.
A被创建了一个实例A1,并且放在了任务9中。
此时任务栈中内容为:
A
点击界面上启动A的按钮,看到输出:
Task:9(380): A2 created.
A再次被创建了一个新的实例A2,仍然放在任务9中。
此时任务栈中内容为:
AA
点击启动B的按钮,
Task:9(380): B1 created.
B被创建一个新的实例B1,仍然放在任务9中。
此时任务栈内容为:
AAB
再启动B,
Task:9(380): B2 created.
B又被创建了一个新的实例B2,仍然在任务9中。
任务栈内容为:
AABB
按Back键依次返回桌面,输出依次为:
Task:9(380): B2 destroyed.
Task:9(380): B1 destroyed.
Task:9(380): A2 destroyed.
Task:9(380): A1 destroyed.
任务栈的变化次序为:
AABB
AAB
AA
A
结论:
每个Intent,都创建了一个新的Activity来响应,并且Back时是按照弹栈的顺序处理的。
2.B设置为SingleTop,启动Ap:
Task:10(409): A1 created.
再启动A:
Task:10(409): A2 created.
再启动B:
Task:10(409): B1 created.
再启动B:
Task:10(409): B1 onNewIntent.
结论:
当B不在栈顶时,仍然会创建B的新实例。
当B在栈顶时,请求启动B的Intent并没有触发创建B的新实例,而是触发了栈顶B的onNewIntent()
按Back键依次返回桌面,输出依次为:
Task:10(409): B1 destroyed.
Task:10(409): A2 destroyed.
Task:10(409): A1 destroyed.
结论:
虽然发送了四次Intent,但实际上只创建了3个Activity,所以只有3次销毁输出。
3.B设置为SingleTask,启动Ap:
Task:11(438): A1 created.
启动B:
Task:11(438): B1 created.
启动A:
Task:11(438): A2 created.
启动B:
Task:11(438): B1 onNewIntent.
Task:11(438): A2 destroyed.
结论:
当B已经存在时,再次请求B会触发已经存在的B1实例的onNewIntent;
并且如果B所在的任务栈上面有其他Activity,那么其他Activity会被销毁。
4.B设置为SingleInstance,启动Ap:
Task:12(466): A1 created.
启动B,此时新创建了任务栈13,和先前的A1并不在一个任务。
Task:13(466): B1 created.
再启动B,没有创建新实例:
Task:13(466): B1 onNewIntent.
再启动A,再旧的任务12中创建了A的新实例
Task:12(466): A2 created.
按下Back键:
Task:12(466): A2 destroyed.
看输出和前面没区别,但是注意界面,此时不是切换到B界面,因为此时活动任务是12,所以A1被显示了出来。
再次按下Back键:
Task:12(466): A1 destroyed.
此时因为任务12已经结束,B1所在的任务13成为活动任务,B1的界面才被显示出来。
-----------------------------------------------------------------
Activity的归属,也就是Activity应该在哪个Task中,Activity与Task的吸附关系。我们知道,一般情况下在同一个应用中,启动的Activity都在同一个Task中,它们在该Task中度过自己的生命周期,这些Activity是从一而终的好榜样。
那么为什么我们创建的Activity会进入这个Task中?它们会转到其它的Task中吗?如果转到其它的Task中,它们会到什么样的Task中去?
解决这些问题的关键,在于每个Activity的taskAffinity属性。
每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式的指明该 Activity的taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果 Application也没有指明,那么该taskAffinity的值就等于包名。而Task也有自己的affinity属性,它的值等于它的根
Activity的taskAffinity的值。
一开始,创建的Activity都会在创建它的Task中,并且大部分都在这里度过了它的整个生命。然而有一些情况,创建的Activity会被分配其它的Task中去,有的甚至,本来在一个Task中,之后出现了转移。我们首先分析一下android文档给我们介绍的两种情况。
第一种情况。如果该Activity的allowTaskReparenting设置为true,它进入后台,当一个和它有相同affinity的Task进入前台时,它会重新宿主,进入到该前台的task中。
我们验证一下这种情况。
Application Activity taskAffinity allowTaskReparenting
application1 Activity1 com.winuxxan.affinity true
application2 Activity2 com.winuxxan.affinity false
我们创建两个工程,application1和application2,分别含有Activity1和Activity2,它们的taskAffinity相同,Activity1的allowTaskReparenting为true。
首先,我们启动application1,加载Activity1,然后按Home键,使该task(假设为task1)进入后台。然后启动application2,默认加载Activity2。
我们看到了什么现象?没错,本来应该是显示Activity2,但是我们却看到了Activity1。实际上Activity2也被加载了,只是Activity1重新宿主,所以看到了Activity1。
第二种情况。如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。
我们来做一个测试。
我们首先写一个应用,它有两个Activity(Activity1和Activity2),AndroidManifest.xml如下:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Activity1"
android:taskAffinity="com.winuxxan.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);
}
}
然后,我们再写一个应用MyActivity,它包含一个Activity(MyActivity),AndroidManifest.xml如下:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MyActivity"
android:taskAffinity="com.winuxxan.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。然后按返回键。
我们发现,我们进入Activity的顺序为Activity2->Activity1,而返回时顺序为 Activity1->MyActivity。这就说明了一个问题,Activity1在启动时,重新宿主到了MyActivity所在的Task 中去了。
以上是验证了文档中提出的两种TaskAffinity的用法。
下面就是见证奇迹的时刻,同志们,不要眨眼!
我们现在将上一文中的launchMode和本文讲的taskAffinity结合起来。
首先是singleTask加载模式与taskAffinity的结合。
我们还是用上一文中的singleTask的代码,这里就不在列出来了,请读者自己查阅上一文。唯一不同的就是,我们为MyActivity和Activity1设置成相同的taskAffinity,重新执行上文的测试。
我们发现测试结果令我们惊讶:从同一应用程序启动singleTask和不同应用程序启动的结果完全与上文讲的相反!
我们经过思考,就可以把从同一应用程序执行和从不同应用程序执行另种方式同一起来,得到一个结论:
当一个应用程序加载一个singleTask模式的Activity时,首先该Activity会检查是否存在与它的taskAffinity相同的Task。
1、如果存在,那么检查是否实例化,如果已经实例化,那么销毁在该Activity以上的Activity并调用onNewIntent。如果没有实例化,那么该Activity实例化并入栈。
2、如果不存在,那么就重新创建Task,并入栈。
用一个流程来表示:
然后我们来检测singleInstance模式融入taskAffinity时的情况,我们也是用上文中测试singleInstance的例子,在此不列出,读者翻阅前文查阅。唯一不同的是,我们将MyActivity和Activity2设置成相同的taskAffinity。
我们发现测试结果也有一定的出入,就是,当从singleInstance中启动Activity时,并没用重新创建一个Task,而是进入了和它具有相同affinity的MyActivity所在的Task。
于是,我们也能得到以下结论:
1、当一个应用程序加载一个singleInstance模式的Activity时,如果该Activity没有被实例化,那么就重新创建一个Task,并入栈,如果已经被实例化,那么就调用该Activity的onNewIntent;
2、singleInstance的Activity所在的Task不允许存在其他Activity,任何从该Activity加载的其它 Actiivty(假设为Activity2)都会被放入其它的Task中,如果存在与Activity2相同affinity的Task,则在该 Task内创建Activity2。如果不存在,则重新生成新的Task并入栈。
界面如下:(上面的一行文字用来识别是在A界面还是在B界面)
在代码中实现onCreate():
Java代码
Button btnA = (Button)findViewById(R.id.btnA);
btnA.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(A.this, A.class));
}
});
Button btnB = (Button)findViewById(R.id.btnB);
btnB.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(A.this, B.class));
}
});
Log.e("Task:"+getTaskId(), "A" + mId + " created.");
onNewIntent():
Java代码
Log.e("Task:"+getTaskId(), "A" + mId + " onNewIntent.");
onDestroy():
Java代码
Log.e("Task:"+getTaskId(), "A" + mId + " destroyed.");
这样就可以直观的从Logcat输出中看到Activity的创建、销毁以及请求的响应。
1.两个Actiity的LaunchMode都为默认的standard,启动Ap,看到输出:
Task:9(380): A1 created.
A被创建了一个实例A1,并且放在了任务9中。
此时任务栈中内容为:
A
点击界面上启动A的按钮,看到输出:
Task:9(380): A2 created.
A再次被创建了一个新的实例A2,仍然放在任务9中。
此时任务栈中内容为:
AA
点击启动B的按钮,
Task:9(380): B1 created.
B被创建一个新的实例B1,仍然放在任务9中。
此时任务栈内容为:
AAB
再启动B,
Task:9(380): B2 created.
B又被创建了一个新的实例B2,仍然在任务9中。
任务栈内容为:
AABB
按Back键依次返回桌面,输出依次为:
Task:9(380): B2 destroyed.
Task:9(380): B1 destroyed.
Task:9(380): A2 destroyed.
Task:9(380): A1 destroyed.
任务栈的变化次序为:
AABB
AAB
AA
A
结论:
每个Intent,都创建了一个新的Activity来响应,并且Back时是按照弹栈的顺序处理的。
2.B设置为SingleTop,启动Ap:
Task:10(409): A1 created.
再启动A:
Task:10(409): A2 created.
再启动B:
Task:10(409): B1 created.
再启动B:
Task:10(409): B1 onNewIntent.
结论:
当B不在栈顶时,仍然会创建B的新实例。
当B在栈顶时,请求启动B的Intent并没有触发创建B的新实例,而是触发了栈顶B的onNewIntent()
按Back键依次返回桌面,输出依次为:
Task:10(409): B1 destroyed.
Task:10(409): A2 destroyed.
Task:10(409): A1 destroyed.
结论:
虽然发送了四次Intent,但实际上只创建了3个Activity,所以只有3次销毁输出。
3.B设置为SingleTask,启动Ap:
Task:11(438): A1 created.
启动B:
Task:11(438): B1 created.
启动A:
Task:11(438): A2 created.
启动B:
Task:11(438): B1 onNewIntent.
Task:11(438): A2 destroyed.
结论:
当B已经存在时,再次请求B会触发已经存在的B1实例的onNewIntent;
并且如果B所在的任务栈上面有其他Activity,那么其他Activity会被销毁。
4.B设置为SingleInstance,启动Ap:
Task:12(466): A1 created.
启动B,此时新创建了任务栈13,和先前的A1并不在一个任务。
Task:13(466): B1 created.
再启动B,没有创建新实例:
Task:13(466): B1 onNewIntent.
再启动A,再旧的任务12中创建了A的新实例
Task:12(466): A2 created.
按下Back键:
Task:12(466): A2 destroyed.
看输出和前面没区别,但是注意界面,此时不是切换到B界面,因为此时活动任务是12,所以A1被显示了出来。
再次按下Back键:
Task:12(466): A1 destroyed.
此时因为任务12已经结束,B1所在的任务13成为活动任务,B1的界面才被显示出来。
-----------------------------------------------------------------
Activity的归属,也就是Activity应该在哪个Task中,Activity与Task的吸附关系。我们知道,一般情况下在同一个应用中,启动的Activity都在同一个Task中,它们在该Task中度过自己的生命周期,这些Activity是从一而终的好榜样。
那么为什么我们创建的Activity会进入这个Task中?它们会转到其它的Task中吗?如果转到其它的Task中,它们会到什么样的Task中去?
解决这些问题的关键,在于每个Activity的taskAffinity属性。
每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式的指明该 Activity的taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果 Application也没有指明,那么该taskAffinity的值就等于包名。而Task也有自己的affinity属性,它的值等于它的根
Activity的taskAffinity的值。
一开始,创建的Activity都会在创建它的Task中,并且大部分都在这里度过了它的整个生命。然而有一些情况,创建的Activity会被分配其它的Task中去,有的甚至,本来在一个Task中,之后出现了转移。我们首先分析一下android文档给我们介绍的两种情况。
第一种情况。如果该Activity的allowTaskReparenting设置为true,它进入后台,当一个和它有相同affinity的Task进入前台时,它会重新宿主,进入到该前台的task中。
我们验证一下这种情况。
Application Activity taskAffinity allowTaskReparenting
application1 Activity1 com.winuxxan.affinity true
application2 Activity2 com.winuxxan.affinity false
我们创建两个工程,application1和application2,分别含有Activity1和Activity2,它们的taskAffinity相同,Activity1的allowTaskReparenting为true。
首先,我们启动application1,加载Activity1,然后按Home键,使该task(假设为task1)进入后台。然后启动application2,默认加载Activity2。
我们看到了什么现象?没错,本来应该是显示Activity2,但是我们却看到了Activity1。实际上Activity2也被加载了,只是Activity1重新宿主,所以看到了Activity1。
第二种情况。如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。
我们来做一个测试。
我们首先写一个应用,它有两个Activity(Activity1和Activity2),AndroidManifest.xml如下:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Activity1"
android:taskAffinity="com.winuxxan.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);
}
}
然后,我们再写一个应用MyActivity,它包含一个Activity(MyActivity),AndroidManifest.xml如下:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MyActivity"
android:taskAffinity="com.winuxxan.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。然后按返回键。
我们发现,我们进入Activity的顺序为Activity2->Activity1,而返回时顺序为 Activity1->MyActivity。这就说明了一个问题,Activity1在启动时,重新宿主到了MyActivity所在的Task 中去了。
以上是验证了文档中提出的两种TaskAffinity的用法。
下面就是见证奇迹的时刻,同志们,不要眨眼!
我们现在将上一文中的launchMode和本文讲的taskAffinity结合起来。
首先是singleTask加载模式与taskAffinity的结合。
我们还是用上一文中的singleTask的代码,这里就不在列出来了,请读者自己查阅上一文。唯一不同的就是,我们为MyActivity和Activity1设置成相同的taskAffinity,重新执行上文的测试。
我们发现测试结果令我们惊讶:从同一应用程序启动singleTask和不同应用程序启动的结果完全与上文讲的相反!
我们经过思考,就可以把从同一应用程序执行和从不同应用程序执行另种方式同一起来,得到一个结论:
当一个应用程序加载一个singleTask模式的Activity时,首先该Activity会检查是否存在与它的taskAffinity相同的Task。
1、如果存在,那么检查是否实例化,如果已经实例化,那么销毁在该Activity以上的Activity并调用onNewIntent。如果没有实例化,那么该Activity实例化并入栈。
2、如果不存在,那么就重新创建Task,并入栈。
用一个流程来表示:
然后我们来检测singleInstance模式融入taskAffinity时的情况,我们也是用上文中测试singleInstance的例子,在此不列出,读者翻阅前文查阅。唯一不同的是,我们将MyActivity和Activity2设置成相同的taskAffinity。
我们发现测试结果也有一定的出入,就是,当从singleInstance中启动Activity时,并没用重新创建一个Task,而是进入了和它具有相同affinity的MyActivity所在的Task。
于是,我们也能得到以下结论:
1、当一个应用程序加载一个singleInstance模式的Activity时,如果该Activity没有被实例化,那么就重新创建一个Task,并入栈,如果已经被实例化,那么就调用该Activity的onNewIntent;
2、singleInstance的Activity所在的Task不允许存在其他Activity,任何从该Activity加载的其它 Actiivty(假设为Activity2)都会被放入其它的Task中,如果存在与Activity2相同affinity的Task,则在该 Task内创建Activity2。如果不存在,则重新生成新的Task并入栈。
相关文章推荐
- android Task Activity launchMode="singleTask" singleInstance 代码分析
- 一个由Activity launchMode="singleTask"属性引起的血案(Android)
- launchMode affinity allowTaskReparent
- android:launchMode="singleTask" 与 onNewIntent(Intent intent) 的用法
- android:launchMode="singleTask" intent获取到的值没有更新
- 跳转activity设置为launchMode="singleTask"时,主activity不执行onActivityResult
- Activity launchMode之singleTask与taskAffinity扫盲
- 设置android:clearTaskOnLaunch="true"属性对Activity行为的影响
- android:launchMode="singleTask" 与 onNewIntent(Intent intent) 的用法
- Android Activity launchMode singleTask/singleTop/singleInstance/standard 讲解
- android Activity android:launchMode="singleInstance" 与 startActivityForResult
- launchmode & clearTaskOnLaunch & finishOnTaskLaunch
- Activity launchMode="SingleTaks"的秘密
- Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance
- android:launchMode="singleTask" 与 onNewIntent(Intent intent) 的用法
- android:launchMode="singleTask" 与 onNewIntent(Intent intent) 的用法
- android task栈和activity的关系(Task/launchMode)
- android activity Launch Mode & TaskAffinity解析
- Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance
- Activity设置启动模式android:launchMode="singleInstance" 按home键后会销毁