activity啟動(dòng)模式你所不知道的異常情況

前言

雖然了解activity的四種啟動(dòng)模式念逞,但是在一些復(fù)雜場(chǎng)景下候衍,各種啟動(dòng)模式會(huì)出現(xiàn)的現(xiàn)象蛇尚,以及現(xiàn)象的原因并不清楚摆碉,再加上個(gè)taskAffinity launchMode clearTaskOnLaunch 這些參數(shù)會(huì)使得更加懵逼塘匣。所以根據(jù)在實(shí)際應(yīng)用中遇到的問題總結(jié)一下。

主要內(nèi)容

要講啟動(dòng)模式需要從Task ,taskAffinity 以及l(fā)aunchMode巷帝,還有標(biāo)簽四個(gè)方面入手忌卤,看這四個(gè)之前的關(guān)聯(lián)以及影響。
在這里插入圖片描述

Task

task跟activity的啟動(dòng)息息相關(guān)楞泼,因?yàn)閍ctivity啟動(dòng)后都是放在task里面進(jìn)行管理的,task的數(shù)據(jù)結(jié)構(gòu)是stack的驰徊,先進(jìn)后出,新創(chuàng)建的activity放在task的頂部堕阔,如下圖打開ActivityA->activityB->activityC:
在這里插入圖片描述

task的特點(diǎn):

  1. activity的集合
  2. 以棧的形式對(duì)activity進(jìn)行管理(back stack)
  3. task里面至少包含一個(gè)activity
  4. 新創(chuàng)建的activity放在棧頂辣垒。
  5. 每一個(gè)task都有稱為Affinity的name。

TaskAffinity

taskAffinity是activity可以在manifest文件里面設(shè)置的屬性.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.coroutinescopedemo">

    <application android:allowBackup="true">
        <activity
            android:name=".MainActivity"
            android:taskAffinity="hanking.edu">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
復(fù)制代碼

用來確定啟動(dòng)的activity屬于哪個(gè)task印蔬,或者確定task的名稱勋桶。具體的功能如下:

  1. 用來決定持有activity的task是哪個(gè)。

  2. 默認(rèn)情況下一個(gè)app里面的activity都有相同的affinity值(package name)

  3. task的affinity值由觸發(fā)創(chuàng)建task的activity的affinity值決定侥猬。(也被稱為root activity)

taskAffinity用來確定activity所在棧的名字例驹,是不是任何時(shí)候都會(huì)生效?看下默認(rèn)情況下的兩個(gè)activity設(shè)置不同的affinity會(huì)發(fā)生什么情況退唠。

1鹃锈、給activity設(shè)置task affinity 如下創(chuàng)建了activityA和activityB,其中給activityB設(shè)置了task Affinity值為com.something.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myApp">

    <application
        android:allowBackup="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ActivityA">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
復(fù)制代碼

流程:打開activityA 從activityA跳轉(zhuǎn)到ActivityB瞧预,然后打印task的情況屎债。 通過adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'打印情況如下

adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
    Running activities (most recent first):
      TaskRecord{5938ae7 #1669 A=com.example.coroutinescopedemo U=0 StackId=287 sz=2}
        Run #1: ActivityRecord{5d93c09 u0 com.myApp/.ActivityB t1669}
        Run #0: ActivityRecord{5ce5f59 u0 com.myApp/.ActivityA t1669}

復(fù)制代碼
在這里插入圖片描述

有上面流程可以知道仅政,activity默認(rèn)啟動(dòng)情況下加task affinity屬性并不會(huì)新建task,也不會(huì)改變task名稱盆驹,task的名稱和taskRoot的activity中設(shè)置的task affinity值一致圆丹,如果沒設(shè)置默認(rèn)就是包名,這里taskRoot Activity是activityA。

2躯喇、添加FLAG_ACTIVITY_NEW_TASK 由上面可見辫封,默認(rèn)模式下就算給activity添加了taskAffinity屬性也不會(huì)多創(chuàng)建一個(gè)task,原因是這個(gè)taskAffinity應(yīng)該和FLAG_ACTIVITY_NEW_TASK一起使用才會(huì)創(chuàng)建新task廉丽。

 val i = Intent(this, ActivityB::class.java)
            i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            startActivity(i)
復(fù)制代碼

在ActivityA中啟動(dòng)ActivityB的時(shí)候加上Intent.FLAG_ACTIVITY_NEW_TASK的flag,再嘗試從ActivityA打開ActivityB倦微。

  Running activities (most recent first):
      TaskRecord{59b097b #1675 A=com.something U=0 StackId=293 sz=1}
        Run #0: ActivityRecord{5d93041 u0 com.example.myApp/.ActivityB t1675}
    Running activities (most recent first):
      TaskRecord{59b09a0 #1674 A=com.example.myApp U=0 StackId=292 sz=1}
        Run #0: ActivityRecord{5d2de41 u0 com.example.myApp/.ActivityA t1674}

復(fù)制代碼

由上面的信息可以看到有兩個(gè)task,ActivityB所在的task 名稱是com.something, stackId=293, ActivityA所在的task名稱是com.example.myApp stackId=292
在這里插入圖片描述

思考:加上Intent.FLAG_ACTIVITY_NEW_TASK的tag后由于啟動(dòng)了一個(gè)新的task正压,這時(shí)候退到任務(wù)管理器可以看到activityA和activityB所在的task欣福,如果這個(gè)時(shí)候點(diǎn)開activityB再點(diǎn)擊返回還會(huì)返回到activityA嗎? 答案是不會(huì)焦履?因?yàn)閺腶ctivityA打開activityB后再切到后臺(tái)劣欢,這個(gè)時(shí)候這兩個(gè)activity的task都屬于background狀態(tài),再打開activityB的task裁良,activityB的task屬于foreground task,返回會(huì)直接返回到桌面凿将。

activity啟動(dòng)模式

activity的啟動(dòng)模式一般分為以下四種,四種模式的特點(diǎn)如下:
在這里插入圖片描述

Standard:

activity默認(rèn)的啟動(dòng)模式价脾,在standard模式下每次打開一個(gè)activity的時(shí)候都會(huì)生成一個(gè)新的實(shí)例牧抵。 如下已經(jīng)有了A,B,C,D在stack中,再啟動(dòng)B侨把,B是standard模式犀变。 A →B→ C→D 啟動(dòng)B, A → B → C→D→ B 可以看到會(huì)再次生成B的實(shí)例秋柄,并放到棧頂获枝。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myApp">

    <application
        android:allowBackup="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ActivityA">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
復(fù)制代碼
 val i = Intent(this, ActivityB::class.java)
            startActivity(i)
復(fù)制代碼

在標(biāo)準(zhǔn)模式下啟動(dòng)ActivityA->ActivityB->activityB如下圖:
在這里插入圖片描述

standard模式看起來非常簡(jiǎn)單,每次生成activity實(shí)例并放在棧頂骇笔,但是當(dāng)standard模式和flag一起使用的時(shí)候又會(huì)產(chǎn)生很多不一樣的效果省店。 1、standard+Intent.FLAG_ACTIVITY_NEW_TASK 按照下面的方式啟動(dòng)activity

啟動(dòng) Activity A 
ActivityA 啟動(dòng) ActivityB (no flags)
ActivityB 啟動(dòng) Activity A
ActivityA 啟動(dòng) Activity B (with flag NEW_TASK)
ActivityB 啟動(dòng) ActivityA
復(fù)制代碼
在這里插入圖片描述

由上圖可知當(dāng)activityA啟動(dòng)activityB笨触,并且此時(shí)intent加上NEW_TASK標(biāo)簽時(shí)懦傍,會(huì)生成一個(gè)新的task com.something,并且activityB作為taskRootActivity,此時(shí)activityB再啟動(dòng)activityA芦劣,activityA也會(huì)在com.something的棧上生成實(shí)例粗俱。

SingleTop

如果需要打開的activity的實(shí)例已經(jīng)處于當(dāng)前棧頂,那么會(huì)復(fù)用當(dāng)前棧頂?shù)腶ctivity虚吟,不會(huì)重新創(chuàng)建activity寸认,但是會(huì)通過調(diào)用onNewIntent().所以如果需要刷新頁面數(shù)據(jù)签财,就要在onNewIntent().進(jìn)行處理。如果棧頂?shù)腶ctivity和需要打開的activity不相同,那么會(huì)重新創(chuàng)建一個(gè)activity,并進(jìn)棧终吼。 假設(shè)棧里面已經(jīng)有A ,B,C幾個(gè)activity, . A →B →C 如果需要再打開activity B那么如下: A →B →C →B 如果這個(gè)時(shí)候再調(diào)用打開activity B會(huì)直接復(fù)用棧頂?shù)腂油宜,并且調(diào)用B的onNewIntent()方法掂碱,棧如下 A →B →C →B

Step 1: Launch A -> A
Step 2: Launch B -> A-B 
Step 3: Launch C -> A-B-C 
Step 4: Launch B -> A-B-C-B
Step 5: Launch B -> A-B-C-B
復(fù)制代碼

singleTop總結(jié)一句:就是復(fù)用棧頂activity怜姿。

SingleTask

如果棧中存在這個(gè)Activity的實(shí)例就會(huì)復(fù)用這個(gè)Activity,不管它是否位于棧頂疼燥,復(fù)用時(shí)沧卢,會(huì)將它上面的Activity全部出棧,并且會(huì)回調(diào)該實(shí)例的onNewIntent方法醉者。其實(shí)這個(gè)過程還存在一個(gè)任務(wù)棧的匹配但狭,因?yàn)檫@個(gè)模式啟動(dòng)時(shí),會(huì)在自己需要的任務(wù)棧中尋找實(shí)例撬即,這個(gè)任務(wù)棧就是通過taskAffinity屬性指定立磁。如果這個(gè)任務(wù)棧不存在,則會(huì)創(chuàng)建這個(gè)任務(wù)棧剥槐。

Step 1: Launch A -> A
Step 2: Launch B -> A-B 
Step 3: Launch C -> A-B-C 
Step 4: Launch B -> A-B
Step 5: Launch B -> A-B*
復(fù)制代碼

如上當(dāng)棧中有A-B-C此時(shí)再啟動(dòng)B唱歧,會(huì)遍歷棧,然后找到B粒竖,將B上面的C出棧颅崩。

singleInstance

singleInstance模式下的Activity會(huì)單獨(dú)占用一個(gè)Task棧,具有全局唯一性蕊苗,即整個(gè)系統(tǒng)中就這么一個(gè)實(shí)例沿后,由于棧內(nèi)復(fù)用的特性,后續(xù)的請(qǐng)求均不會(huì)創(chuàng)建新的Activity實(shí)例朽砰,除非這個(gè)特殊的任務(wù)棧被銷毀了尖滚。以singleInstance模式啟動(dòng)的Activity在整個(gè)系統(tǒng)中是單例的,如果在啟動(dòng)這樣的Activiyt時(shí)瞧柔,已經(jīng)存在了一個(gè)實(shí)例熔掺,那么會(huì)把它所在的任務(wù)調(diào)度到前臺(tái),重用這個(gè)實(shí)例非剃。 假設(shè)有三個(gè)activity:A,B,C置逻,B是singleInstance的,

Step 1: Launch A -> Task 1: A
Step 2: Launch B -> Task 1: A
                   Task 2: B    // Visible to the user
Step 3: Launch C -> Task 1: A-C  // Visible to the user
                   Task 2: B
Step 4: Launch B -> Task 1: A-C
                   Task 2: B*   // Visible to the user
復(fù)制代碼

FLAG_ACTIVITY_NEW_TASK

Start the activity in a new task. If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent().

復(fù)雜場(chǎng)景分析

上面都是簡(jiǎn)單的場(chǎng)景备绽,如果選擇更加復(fù)雜的場(chǎng)景券坞,又會(huì)出現(xiàn)意想不到的現(xiàn)象鬓催。
在這里插入圖片描述

1、task切換后打不開activity

  1. 啟動(dòng) ActivityA
  2. Activity A 啟動(dòng) Activity B
  3. Activity B 啟動(dòng) Activity C with FLAG_NEW_TASK
  4. Activity C 啟動(dòng) Activity D
  5. 用戶切換到 com.myApp
  6. Activity B 啟動(dòng) Activity C with FLAG_NEW_TASK
在這里插入圖片描述

上面有個(gè)比較奇怪的現(xiàn)象activityB啟動(dòng)activityC恨锚,activityC 啟動(dòng)activityD宇驾,都是new_task方式,C猴伶,D课舍,都是在com.something的task里面,這里正常他挎,但是activityB筝尾,再次調(diào)用activityC的時(shí)候卻沒有啟動(dòng)activityC。 2办桨、task切換后打開多個(gè)activity

在這里插入圖片描述


 1\. 啟動(dòng) Activity A
 2\. Activity A 啟動(dòng) Activity B
 3\. Activity B 啟動(dòng) Activity C with FLAG_NEW_TASK
 4\. Activity C 啟動(dòng) Activity D
 5\. User switches to com.myApp
 6\. Activity B 啟動(dòng) Activity D with FLAG_NEW_TASK

復(fù)制代碼

上面流程啟動(dòng)activity的筹淫,task是什么情況?
在這里插入圖片描述

看上圖呢撞,最后當(dāng)activityB其次啟動(dòng)activityD的時(shí)候又創(chuàng)建了一個(gè)activityD损姜,

當(dāng)activityB啟動(dòng)activityD的時(shí)候?yàn)槭裁磿?huì)新創(chuàng)建一個(gè)activityD的實(shí)例? 這里重新創(chuàng)建activityD的原因是:通過activityC創(chuàng)建activityD的intent和通過activityB創(chuàng)建activityD的intent的不一樣導(dǎo)致的殊霞。也就是說只有當(dāng)intent的一樣時(shí)才不會(huì)創(chuàng)建多個(gè)實(shí)例摧阅。

startActivityForResult 異常

在這里插入圖片描述

activityA是singleInstance,activityB是standard模式,當(dāng)activityA調(diào)用startActivityForResult打開activityB時(shí)绷蹲,activityA收到的回調(diào)函數(shù)onActivityResult能接受activityB的返回結(jié)果嗎棒卷? 正常情況下activityA通過startActivityForResult打開activityB時(shí)的流程如下:

  1. activityA 重寫onActivityResult接受activityB中的返回值。
  2. activityB通過setResult確定返回值瘸右。

3.當(dāng)activityB返回時(shí)activityA的onActivityResult才會(huì)被回調(diào)娇跟。

但是當(dāng)activityA定義為singleInstance時(shí)通過startActivityForResult打開activityB時(shí)如下:
在這里插入圖片描述

因?yàn)閍ctivityA是singleInstance的所以獨(dú)享一個(gè)task,當(dāng)activityA打開activityB時(shí)太颤,也創(chuàng)建一個(gè)新的task苞俘,但是onActivityResult是立馬就回調(diào),不是activityB finish的時(shí)候回調(diào)的龄章。這是什么原因吃谣? 注意:當(dāng)一個(gè)activity 調(diào)用startActivityForResult打開另一個(gè)activity當(dāng)時(shí)另一個(gè)activity在另一個(gè)創(chuàng)建的task里面的時(shí)候,onActivityResult就會(huì)立刻回調(diào)做裙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岗憋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锚贱,更是在濱河造成了極大的恐慌仔戈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異监徘,居然都是意外死亡晋修,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門凰盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墓卦,“玉大人,你說我怎么就攤上這事户敬÷浼簦” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵尿庐,是天一觀的道長(zhǎng)忠怖。 經(jīng)常有香客問我,道長(zhǎng)屁倔,這世上最難降的妖魔是什么脑又? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任暮胧,我火速辦了婚禮锐借,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘往衷。我一直安慰自己钞翔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布席舍。 她就那樣靜靜地躺著布轿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪来颤。 梳的紋絲不亂的頭發(fā)上汰扭,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音福铅,去河邊找鬼萝毛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛滑黔,可吹牛的內(nèi)容都是我干的笆包。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼略荡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼庵佣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汛兜,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤巴粪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肛根,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衡创,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晶通。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璃氢。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖狮辽,靈堂內(nèi)的尸體忽然破棺而出一也,到底是詐尸還是另有隱情,我是刑警寧澤喉脖,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布椰苟,位于F島的核電站,受9級(jí)特大地震影響树叽,放射性物質(zhì)發(fā)生泄漏舆蝴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一题诵、第九天 我趴在偏房一處隱蔽的房頂上張望洁仗。 院中可真熱鬧,春花似錦性锭、人聲如沸赠潦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽她奥。三九已至,卻和暖如春怎棱,著一層夾襖步出監(jiān)牢的瞬間哩俭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工拳恋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凡资,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓诅岩,卻偏偏與公主長(zhǎng)得像讳苦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吩谦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容