【基礎(chǔ)】任務(wù)棧與啟動(dòng)模式

對(duì)于Android的啟動(dòng)模式,似乎是最簡(jiǎn)單的入門知識(shí)了,Standard、SingleTopSingleTask、SingleInstance。
本文重點(diǎn)對(duì)singleTask進(jìn)行加深理解。

入門及使用

任務(wù)棧(Task)

要了解啟動(dòng)模式的具體表現(xiàn)流程,首先要了解任務(wù)棧。官網(wǎng)介紹說任務(wù)與回退棧,這里直接成為任務(wù)棧。

一個(gè)Application一般是由多個(gè)Activity構(gòu)成,而多個(gè)Activity的管理,Android已經(jīng)幫我們管理好了,使用棧方式管理,也就是先進(jìn)后出,啟動(dòng)模式可以理解為四種不同的進(jìn)棧出棧方式。而這個(gè)管理activity的棧成為任務(wù)棧。

  1. 一個(gè)app默認(rèn)只有一個(gè)任務(wù)棧,由系統(tǒng)指定。
  2. 一個(gè)app可以存在多個(gè)任務(wù)棧,需要自己手動(dòng)設(shè)置。多個(gè)任務(wù)棧時(shí),當(dāng)前activity所在棧處于前臺(tái),其余棧處于后臺(tái)。

啟動(dòng)模式

1. standard

<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=".A"
      android:launchMode="standard"/>

standard是android中默認(rèn)的啟動(dòng)模式,如果你新建一個(gè)應(yīng)用,不進(jìn)行任何修改,那么MainActivity的啟動(dòng)模式就是standard。

standard會(huì)進(jìn)行重復(fù)創(chuàng)建實(shí)例,比如應(yīng)用中有Main和A兩個(gè)activity,那么Main -> A -> A -> Main這樣的啟動(dòng)順序,就會(huì)如下圖所示:

standard重復(fù)創(chuàng)建實(shí)例

點(diǎn)擊back返回,則會(huì)正常出棧,直到最后一個(gè)出棧,app關(guān)閉回到桌面。

按back鍵返回示意圖

這種模式一般不推薦使用,會(huì)占用過多內(nèi)存,頁面重復(fù)。

2. singleTop 棧頂復(fù)用

<activity android:name=".A"
      android:launchMode="singleTop"/>

只有這個(gè)activity處于棧頂位置時(shí),不會(huì)重新創(chuàng)建實(shí)例,而是會(huì)使用已經(jīng)創(chuàng)建好的實(shí)例。并且會(huì)調(diào)用onNewIntent方法,可以根據(jù)此方法獲取intent實(shí)例。

singleTop示意圖

應(yīng)用情景:
點(diǎn)擊通知跳轉(zhuǎn)到文章詳情頁,如果當(dāng)前用戶正在文章詳情頁查看時(shí)進(jìn)行點(diǎn)擊,那么可以使用singleTop來確保不會(huì)重復(fù)創(chuàng)建實(shí)例,避免用戶點(diǎn)擊回退鍵時(shí)還是文章詳情頁。

3. singleTask 棧內(nèi)只有一個(gè)實(shí)例

<activity android:name=".Main"
      android:launchMode="singleTask"/>

singleTask模式是棧內(nèi)只允許存在一個(gè)實(shí)例,當(dāng)這個(gè)實(shí)例已經(jīng)創(chuàng)建,會(huì)直接使用這個(gè)實(shí)例,而不會(huì)重新創(chuàng)建,在這個(gè)實(shí)例上方的其他activity也將全部出棧銷毀。重用實(shí)例會(huì)調(diào)用onNewIntent

比如設(shè)置MainsingleTask,打開Main后,再打開A,B,C三個(gè)默認(rèn)standard的Activity,由C打開Main,則棧中只剩下Main。

singleTask
注意1:singleTask 和 taskAffinity

每個(gè)activity都有taskAffinity屬性,如果沒有顯式的指明taskAffinity,那么就等于Application指明的taskAffinity,而如果Application也沒有指明,那么該taskAffinity的值就等于應(yīng)用的包名。

默認(rèn)情況下singleTask所在的任務(wù)棧就是app啟動(dòng)的默認(rèn)棧,而當(dāng)設(shè)置了taskAffinity后,那么啟動(dòng)設(shè)置singleTask的activity,那么就會(huì)新創(chuàng)建一個(gè)棧來存放activity。

<activity android:name=".A"
    android:launchMode="singleTask"
    android:taskAffinity=".myNewTask"/>

這樣一個(gè)app就存在兩個(gè)任務(wù)棧。

啟動(dòng)流程是這樣的:
a)判斷當(dāng)前任務(wù)棧taskAffinity是否為.myNewTask,如果是的話,直接將該activity置于當(dāng)前任務(wù)棧棧頂
b)如果當(dāng)前任務(wù)棧taskAffinity不為.myNewTask,則檢查是否存在一個(gè)任務(wù)棧taskAffinity值為.myNewTask,如存在,則將該任務(wù)棧移到前臺(tái),并將該activity置于棧頂
c)如果并不存在一個(gè)任務(wù)棧taskAffinity值為.myNewTask,那么將會(huì)為該activity創(chuàng)建一個(gè)新的任務(wù)棧,該任務(wù)棧顯示在前臺(tái)

注意2:SingleTask && MAIN啟動(dòng)

每個(gè)app都會(huì)存在一個(gè)啟動(dòng)的activity,即點(diǎn)擊圖標(biāo)進(jìn)入的第一個(gè)activity。

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

當(dāng)此時(shí)的MainActivity設(shè)置android:launchMode="singleTask"時(shí),會(huì)產(chǎn)生這樣一個(gè)現(xiàn)象:回退Home后重新打開app,總是會(huì)重新打開,而不記錄最后打開的頁面。

這個(gè)其實(shí)就是由于棧內(nèi)存在一個(gè)實(shí)例導(dǎo)致,因?yàn)橹耙呀?jīng)啟動(dòng)過app,也就是后臺(tái)存在這個(gè)app的任務(wù)棧,那么回退到Home再重新打開,首先會(huì)找到這個(gè)App對(duì)應(yīng)的任務(wù)棧,而后由于啟動(dòng)的MainActvitySingleTask,在任務(wù)棧中已經(jīng)存在,那么會(huì)直接使用這個(gè)存在的實(shí)例,又因?yàn)?code>MainActivity處于棧底,那么啟動(dòng)這個(gè)已經(jīng)存在的實(shí)例會(huì)將上方所有的activity都清除出棧

注意3:allowTaskReparenting && taskAffinity

activitytaskAffinity,默認(rèn)指向applicationtaskAffinity,默認(rèn)是mainfestpackage值,也就是常說的包名。

allowTaskReparenting這個(gè)屬性很有意思,可以將activity由一個(gè)task遷移到另一個(gè)task,而將activity遷移到另一個(gè)task是又條件的:

  1. 首先指定android:allowTaskReparenting="true"
  2. 指定android:taskAffinity=".xx"這個(gè).xx并不是你應(yīng)用本身的taskAffinity,或者不同應(yīng)用默認(rèn)taskAffinity本身就不同,可以不指定taskAffinity
  3. 發(fā)生Task Reset,點(diǎn)擊Home再啟動(dòng)APP可發(fā)生Task Reset
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.taskb">

    <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=".OtherTaskActivity"
            android:allowTaskReparenting="true"
            android:taskAffinity=".othertask"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

taskAffinity屬性是遷移后的所指棧。

根據(jù)上述Mainfest配置,執(zhí)行流程:

  1. 首先點(diǎn)擊APP圖標(biāo)啟動(dòng)MainActivity,創(chuàng)建一個(gè)taskAffinitycom.example.taskb的任務(wù)棧,MainActivity入棧。
    啟動(dòng)APP后棧狀態(tài)
  2. MainActivity啟動(dòng)OtherTaskActivity,那么他還是會(huì)在應(yīng)用的棧中入棧。
    啟動(dòng)OtherTaskActivity
  3. 點(diǎn)擊Home,發(fā)生Task Reset,應(yīng)用com.example.taskb任務(wù)棧處于后臺(tái),由于OtherTaskActivity屬性allowTaskReparenting="true" && taskAffinity=".othertask",所以此時(shí)這個(gè)應(yīng)用后臺(tái)將存在兩個(gè)任務(wù)棧。
    兩個(gè)任務(wù)棧
  4. 點(diǎn)擊APP圖標(biāo)重新啟動(dòng)應(yīng)用,com.example.taskb任務(wù)棧處于前臺(tái),顯示MainActivity,com.example.taskb.othertask任務(wù)棧處于后臺(tái),不顯示。當(dāng)按回退鍵時(shí),com.example.taskb任務(wù)棧清除,com.example.taskb.othertask任務(wù)棧回到前臺(tái),顯示OtherTaskActivity。
注意4: taskAffinity起作用只有兩種情況
  1. 設(shè)置launchMode="singleTask"或者intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  2. 設(shè)置allowTaskReparenting=true并滿足前面所說的情況。

應(yīng)用場(chǎng)景:
activity設(shè)置SingleTask,退出應(yīng)用清除棧。適合閃屏頁+主頁模式,閃屏頁為MAIN入口,跳轉(zhuǎn)到主頁前finish閃屏頁,主頁啟動(dòng)模式為SingleTask,可以實(shí)現(xiàn)保存應(yīng)用狀態(tài)。

4. singleInstance 棧內(nèi)有且只有一個(gè)實(shí)例,并可復(fù)用

任務(wù)棧中只有一個(gè)activity,如果存在,那么直接使用實(shí)例,調(diào)用onNewIntent方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容