轉(zhuǎn)載請(qǐng)注明出處(謝謝):
http://blog.csdn.net/javazejian/article/details/52072131
通過上一篇文件的分析,我們對(duì)Activity的啟動(dòng)模式有了比較清晰的了解后降淮,本篇我們將繼續(xù)對(duì)Activity啟動(dòng)模式的相關(guān)參數(shù)和任務(wù)棧分析佳鳖,接下來我們就繼續(xù)上一篇的問題系吩,如何通過taskAffinity屬性在同一個(gè)應(yīng)用中創(chuàng)建多個(gè)任務(wù)棧進(jìn)行探究穿挨。
任務(wù)棧之taskAffinity屬性
TaskAffinity特點(diǎn)如下:
- TaskAffinity 參數(shù)標(biāo)識(shí)著Activity所需要的任務(wù)棧的名稱尊搬,默認(rèn)情況下土涝,一個(gè)應(yīng)用中所有Activity所需要的任務(wù)棧名稱都為該應(yīng)用的包名但壮。
- TaskAffinity 屬性一般跟singleTask模式或者跟allowTaskReparenting屬性結(jié)合使用,在其他情況下沒有實(shí)際意義胳施。
- TaskAffinity屬性的值不能與當(dāng)前應(yīng)用包名相同舞肆,否則其值跟作廢沒兩樣筷登。
TaskAffinity和singleTask啟動(dòng)模式結(jié)合使用
當(dāng)TaskAffinity和singleTask啟動(dòng)模式結(jié)合使用時(shí)前方,當(dāng)前Activity的任務(wù)棧名稱將與TaskAffinity屬性指定的值相同惠险,下面我們通過代碼來驗(yàn)證,我們同過MainActivity來啟動(dòng)ActivityA班巩,其中MainActivity啟動(dòng)模式為默認(rèn)模式趣竣,ActivityA啟動(dòng)模式為singleTask遥缕,而TaskAffinity屬性值為android:taskAffinity="com.zejian.singleTask.affinity"
MainActivity和ActivityA代碼如下:
package comzejian.myapplication;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn= (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this,ActivityA.class);
startActivity(i);
}
});
}
}
ActivityA.class 代碼如下:
package comzejian.myapplication;
import android.app.Activity;
import android.os.Bundle;
/**
* Created by zejian
* Time 16/7/26.
* Description:
*/
public class ActivityA extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
}
}
清單文件代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="comzejian.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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=".ActivityA"
android:launchMode="singleTask"
android:taskAffinity="com.zejian.singleTask.affinity"
/>
</application>
</manifest>
現(xiàn)在我們啟動(dòng)MainActivity,后再啟動(dòng)ActivityA户秤,然后我們通過 adb shell dumpsys activity activities
命令查看此時(shí)棧的情況:
我們可以清楚地看到兩個(gè)任務(wù)棧转砖,其中一個(gè)是id=249鲸伴,棧名稱為com.zejian.singleTask.affinity的任務(wù)棧姓赤,該棧包含了ActivityA不铆,另外一個(gè)則是id=248裹唆,棧名為默認(rèn)包名的任務(wù)棧品腹,包含了MainActivity泡垃。到此我們也明白了羡鸥,我們確實(shí)可以通過singleTask與android:taskAffinity屬性相結(jié)合的方式來指定我們Activity所需要的棧名稱惧浴,使相應(yīng)的Activity存在于不同的棧中衷旅,圖解如下:
當(dāng)TaskAffinity和allowTaskReparenting結(jié)合使用
首先我們來聊聊allowTaskReparenting屬性茄袖,它的主要作用是activity的遷移宪祥,即從一個(gè)task遷移到另一個(gè)task,這個(gè)遷移跟activity的taskAffinity有關(guān)藏澳。當(dāng)allowTaskReparenting的值為“true”時(shí)翔悠,則表示Activity能從啟動(dòng)的Task移動(dòng)到有著affinity的Task(當(dāng)這個(gè)Task進(jìn)入到前臺(tái)時(shí))凉驻,當(dāng)allowTaskReparenting的值為“false”,表示它必須呆在啟動(dòng)時(shí)呆在的那個(gè)Task里效诅。如果這個(gè)特性沒有被設(shè)定乱投,元素(當(dāng)然也可以作用在每次activity元素上)上的allowTaskReparenting屬性的值會(huì)應(yīng)用到Activity上戚炫。默認(rèn)值為“false”双肤。這樣說可能還比較難理解茅糜,我們舉個(gè)例子素挽,比如現(xiàn)在有兩個(gè)應(yīng)用A和B预明,A啟動(dòng)了B的一個(gè)ActivityC撰糠,然后按Home鍵回到桌面物喷,再單擊B應(yīng)用時(shí),如果此時(shí)扇丛,allowTaskReparenting的值為“true”帆精,那么這個(gè)時(shí)候并不會(huì)啟動(dòng)B的主Activity卓练,而是直接顯示已被應(yīng)用A啟動(dòng)的ActivityC襟企,我們也可以認(rèn)為ActivityC從A的任務(wù)棧轉(zhuǎn)移到了B的任務(wù)棧中顽悼。這就好比我們?cè)诼愤吺震B(yǎng)了一只與主人走失了的貓,養(yǎng)著養(yǎng)著突然有一天木羹,主人找上門來了坑填,這只貓也就被帶回去了穷遂。我們通過圖解來更好地理解這種情景:
我們通過代碼層面來驗(yàn)證一下蚪黑,我們創(chuàng)建兩個(gè)應(yīng)用分別為ActivityTask(簡(jiǎn)稱A應(yīng)用)和ActivityTask2(簡(jiǎn)稱B應(yīng)用),其中A包含ActivityA掠剑,B包含ActivityC,我們通過ActivityA啟動(dòng)B應(yīng)用中的ActivityC朴译,再回到桌面躬翁,啟動(dòng)B應(yīng)用,此時(shí)我們觀察A應(yīng)用和B應(yīng)用各自棧的變化(因?yàn)锳,B為不同的應(yīng)用所以taskAfinity屬性值肯定不同盒发,所以這里我們就沒必要指定了)。
ActivityA及其清單文件代碼如下:
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
/**
* Created by zejian
* Time 16/7/23.
* Description:
*/
public class ActivityA extends Activity {
private Button btnC;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
btnC= (Button) findViewById(R.id.mainC);
btnC.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName("com.cmcm.activitytask2", "com.cmcm.activitytask2.ActivityC");
intent.setComponent(cn);
startActivity(intent);
}
});
}
}
<activity android:name=".ActivityA">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
ActivityA及其清單文件代碼如下:
package com.cmcm.activitytask;
package com.cmcm.activitytask;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
/**
* Created by zejian
* Time 16/7/23.
* Description:
*/
public class ActivityC extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
}
}
<activity android:name=".ActivityC" android:exported="true"
android:allowTaskReparenting="true">
</activity>
我們通過AcitivityA啟動(dòng)B應(yīng)用的ActivityC后,內(nèi)存中棧的如下:
我們可以看到ActivityA和ActivityC同在一個(gè)棧中,接著我們回到桌面啟動(dòng)B應(yīng)用,此時(shí)內(nèi)存中的任務(wù)棧如下:
我們發(fā)現(xiàn)ActivityC從A應(yīng)用的任務(wù)棧直接移動(dòng)到B應(yīng)用的任務(wù)棧,這也就符合我們前面所說的現(xiàn)象了善涨。而當(dāng)我們修改allowTaskReparenting為false時(shí)窒盐,再運(yùn)行,然后重復(fù)上面的操作钢拧,查看內(nèi)存中任務(wù)棧的變化:
A應(yīng)用啟動(dòng)B應(yīng)用的ActivityC時(shí)
回到桌面再啟動(dòng)B應(yīng)用時(shí)
對(duì)比發(fā)現(xiàn)蟹漓,如果allowTaskReparenting值為false時(shí),ActivityC并不會(huì)直接從A應(yīng)用的任務(wù)棧遷移到B應(yīng)用的任務(wù)棧源内,而是B應(yīng)用直接重新創(chuàng)建了ActivityC的實(shí)例葡粒。到此我們對(duì)于allowTaskReparenting和taskAffinity屬性的了解就已經(jīng)相當(dāng)深入了,不過有點(diǎn)需要說明的是allowTaskReparenting僅限于singleTop和standard模式膜钓,這是因?yàn)橐粋€(gè)activity的affinity屬性由它的taskAffinity屬性定義(代表?xiàng)C┧越唬粋€(gè)task的affinity由它的root activity定義。所以,一個(gè)task的root activity總是擁有和它所在task相同的affinity梅肤。由于以singleTask和singleInstance啟動(dòng)的activity只能是一個(gè)task的root activity撩扒,因此allowTaskReparenting僅限于以standard 和singleTop啟動(dòng)的activity泉手,大家可以自行測(cè)試一下,這里我們就不測(cè)試了哈,下面我們?cè)賮碚f說它們可能應(yīng)用用場(chǎng)景。
TaskAffinity與allowTaskReparenting和singleTask結(jié)合時(shí)可能發(fā)生的應(yīng)用場(chǎng)景
- TaskAffinity與singleTask應(yīng)用場(chǎng)景
假如現(xiàn)在有這么一個(gè)需求,我們的客戶端app正處于后臺(tái)運(yùn)行锰瘸,此時(shí)我們因?yàn)槟承┬枰∏屛⑿耪{(diào)用自己客戶端app的某個(gè)頁(yè)面茸俭,用戶完成相關(guān)操作后,我們不做任何處理,按下回退或者當(dāng)前Activity.finish(),頁(yè)面都會(huì)停留在自己的客戶端(此時(shí)我們的app回退棧不為空),這顯然不符合邏輯的,用戶體驗(yàn)也是相當(dāng)出問題的。我們要求是,回退必須回到微信客戶端,而且要保證不殺死自己的app.這時(shí)候我們的處理方案就是逻卖,設(shè)置當(dāng)前被調(diào)起Activity的屬性為:
LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"
其中com.tencent.mm是借助于工具找到的微信包名盗迟,就是把自己的Activity放到微信默認(rèn)的Task棧里面黔衡,這樣回退時(shí)就會(huì)遵循“Task只要有Activity一定從本Task剩余Activity回退”的原則,不會(huì)回到自己的客戶端;而且也不會(huì)影響自己客戶端本來的Activity和Task邏輯听盖。
- TaskAffinity與allowTaskReparenting應(yīng)用場(chǎng)景
一個(gè)e-mail應(yīng)用消息包含一個(gè)網(wǎng)頁(yè)鏈接腰吟,點(diǎn)擊這個(gè)鏈接將出發(fā)一個(gè)activity來顯示這個(gè)頁(yè)面,雖然這個(gè)activity是瀏覽器應(yīng)用定義的震捣,但是activity由于e-mail應(yīng)用程序加載的壹若,所以在這個(gè)時(shí)候該activity也屬于e-mail這個(gè)task语御。如果e-mail應(yīng)用切換到后臺(tái)骨田,瀏覽器在下次打開時(shí)由于allowTaskReparenting值為true,此時(shí)瀏覽器就會(huì)顯示該activity而不顯示瀏覽器主界面,同時(shí)actvity也將從e-mail的任務(wù)棧遷移到瀏覽器的任務(wù)棧,下次打開e-買了時(shí)并不會(huì)再顯示該activity
??到此TaskAffinity就全部介紹完了片橡,最后我們?cè)賮砹私鈳讉€(gè)跟任務(wù)棧相關(guān)的屬性參數(shù)舆吮;
清空任務(wù)棧
Android系統(tǒng)除了給我提供了TaskAffinity來指定任務(wù)棧名稱外,還給我提供了清空任務(wù)棧的方法,在一般情況下我們只需要在<activity>標(biāo)簽中指明相應(yīng)的屬性值即可挫鸽。
- android:clearTaskOnLaunch
這個(gè)屬性用來標(biāo)記是否從task清除除根Activity之外的所有的Activity,“true”表示清除上遥,“false”表示不清除,默認(rèn)為“false”携狭。這里有點(diǎn)我們必須要注意的搁廓,這個(gè)屬性只對(duì)任務(wù)棧內(nèi)的root Activity起作用境蜕,任務(wù)棧內(nèi)其他的Activity都會(huì)被忽略拉庶。如果android:clearTaskOnLaunch
屬性為“true”慷蠕,每次我們重新進(jìn)入這個(gè)應(yīng)用時(shí),我們只會(huì)看到根Activity盅视,任務(wù)棧中的其他Activity都會(huì)被清除出棧捐名。
??比如一個(gè)應(yīng)用的Activity A,B,C,其中clearTaskOnLaunch設(shè)置為true闹击,C為默認(rèn)值镶蹋,我們依次啟動(dòng)A,B,C,點(diǎn)擊HOME,再在桌面點(diǎn)擊圖標(biāo)赏半。啟動(dòng)的是A贺归,而B,C將都被移除當(dāng)前任務(wù)棧断箫。也就是說拂酣,當(dāng)Activity的屬性clearTaskOnLaunch為true時(shí)將被優(yōu)先啟動(dòng),其余的Activity(B仲义、C)都被移除任務(wù)棧并銷毀婶熬,除非前面A已經(jīng)finish銷毀锣光,后面的已注冊(cè)clearTaskOnLaunch為true的activity(B)才會(huì)生效。
??特別地痰哨,如果我們的應(yīng)用中引用到了其他應(yīng)用的Activity蚌铜,這些Activity設(shè)置了android:allowTaskReparenting
屬性為“true”,則它們會(huì)被重新宿主到有共同affinity的task中饺谬。
- android:finishOnTaskLaunch
finishOnTaskLaunch屬性與clearTaskOnLaunch 有些類似捂刺,它們的區(qū)別是finishOnTaskLaunch是作用在自己身上(把自己移除任務(wù)棧,不影響別的Activity)募寨,而clearTaskOnLaunch則是作用在別人身上(把別的Activity移除任務(wù)棧)族展,如果我們把Activity的android:finishOnTaskLaunch
屬性值設(shè)置為true時(shí),離開這個(gè)Activity所依賴的任務(wù)棧后拔鹰,當(dāng)我們重新返回時(shí)仪缸,該Activity將會(huì)被finish掉,而且其他Activity不會(huì)受到影響格郁。
- android:alwaysRetainTaskState
alwaysRetainTaskState實(shí)際上是給了當(dāng)前Activity所在的任務(wù)棧一個(gè)“免死金牌”腹殿,如果當(dāng)前Activity的android:alwaysRetainTaskState
設(shè)置為true時(shí),那么該Activity所在的任務(wù)棧將不會(huì)受到任何清理命令的影響例书,一直保持當(dāng)前任務(wù)棧的狀態(tài)锣尉。
好了,到此本篇也就完結(jié)决采,相信通過兩篇的記錄我們對(duì)Activity的啟動(dòng)模式和任務(wù)棧都有相對(duì)清晰的了解了哈自沧。
-
Activity啟動(dòng)模式與任務(wù)棧(Task)全面深入記錄(下)
主要參考:
《android開發(fā)藝術(shù)探索》
google官網(wǎng)
http://www.2cto.com/kf/201311/254450.html