Android基礎 - Activity的啓動模式

前言:前一篇文章介紹了Android的activity的生命週期方法,activity的啓動模式也是一個難點,是因爲各種啓動模式和標示位太容易混淆。爲了滿足不同的使用場景,我們必須分清這些啓動模式。這一篇文章,我們來了解一下Activity的啓動模式,也算是一個深入吧,寫的不好,請各位多擔待。

Activity的LanuchMode

在我們新建activity時,如果不選擇啓動模式,則默認使用的是standard模式,這種模式在使用過程中,如果我們多次啓動該activity,則會在棧裏重複創建多個activity實例,單擊back鍵時,則會一一回退,出棧。任務棧是一種“後進先出”的棧結構,意思就是新入棧的任務會放在棧頂,出棧的時候,由棧頂依次出棧。所以我們在使用activity時不可避免的會出現上述問題,導致棧內有多個重複的activity,這是不合理的,所以我們需要分清楚activity的啓動模式,在適合的場景下,使用合適的啓動模式,這就是我們的目的。首先我們先來看一下activity都有哪些啓動模式,然後我們通過實例來分別認識一下它們。啓動模式分別有以下四種模式:

  • standard模式: 標準模式,也就是默認模式,每次啓動一個activity的時候,都會創建一個新的實例,不管這個實例是否存在。被創建的實例,生命週期都符合典型情況下的Activity生命週期。一個任務棧中可以有多個實例,每個實例也可以屬於不同的任務棧。在這種模式下,誰啓動了這個activity,那麼這個activity就會放在誰的任務棧中。當我們用AoolicationContext 去啓動standard模式的activity的時候,就會出現:

Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

這樣的異常。其實很好理解,就是因爲,非Activity類型的Context(如ApplicationContext) 並沒有所謂的任務棧,所以就會啓動失敗。解決方法就是爲啓動的activity指定FLAG_ACTIVITY_NEW_TASK標記位。這樣啓動這個activity的時候就會創建一個新的任務棧,並且將activity放進任務棧。此時activity其實是以singleTask模式啓動的。
具體實例如下:

   //啓動自己
        btn_start_myself.setOnClickListener {
            var sIntent = Intent()
            sIntent.setClass(MainActivity@this,MainActivity::class.java)
            startActivity(sIntent)
        }

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

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

點擊3次啓動自己,然後打開dos命令行,輸入adb shell dumpsys activity,找到如下位置,可以看到,Running activitys 中有4個activity,分別就是剛纔啓動的3個,再加上初始一個。所以使用該模式時,啓動請求都會創建新的activity實例,並放入棧內。

在這裏插入圖片描述

  • singleTop模式: 顧名思義,棧頂複用模式。在這種模式下,如果新activity已經位於棧頂,則新的activity不會被創建,同時會調用它的onNewIntent方法,通過此方法我們可以取出當前請求信息。此模式啓動的activity不會調用onCreate、onStart方法,因爲該activity沒有重建或者重新顯示。
    示例如下:

      將上述的MainActivity的啓動模式改爲singleTop,然後多次點擊啓動自己,我們先看看生命週期回調日誌:
    

2019-02-16 22:59:02.165 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:43)
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.166 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onNewIntent(MainActivity.kt:79)
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onNewIntent
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.167 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onResume(MainActivity.kt:61)
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-16 22:59:02.168 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 22:59:02.373 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Compiler allocated 6MB to compile void android.view.ViewRootImpl.performTraversals()
2019-02-16 22:59:20.825 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Do full code cache collection, code=124KB, data=97KB
2019-02-16 22:59:20.827 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: After code cache collection, code=104KB, data=68KB
2019-02-16 23:00:03.094 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Do partial code cache collection, code=121KB, data=91KB
2019-02-16 23:00:03.095 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: After code cache collection, code=121KB, data=91KB
2019-02-16 23:00:03.095 13586-13604/com.xyd.activitylaunchmodedemo I/zygote64: Increasing code cache capacity to 512KB
2019-02-16 23:00:33.628 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.628 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:43)
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onNewIntent(MainActivity.kt:79)
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onNewIntent
2019-02-16 23:00:33.629 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onResume(MainActivity.kt:61)
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-16 23:00:33.630 13586-13586/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════

然後我們在看看棧內activity的數量:

在這裏插入圖片描述

所以就算是多次點擊,棧內也還是隻有一個activity。

如果我們用另一個activity啓動singleTop模式的activity,看一下是否能複用。
SecondActivity部分代碼:

 //啓動第一個activity
        btn_start_1.setOnClickListener {
            var  sIntent = Intent()
            sIntent.setClass(SecondActivity@this,MainActivity::class.java)
            startActivity(sIntent)
        }

將MainActivity的啓動模式改爲singleTop:

  <activity android:name=".SecondActivity">
        </activity>
        <!--默認就是standard模式-->
        <activity android:name=".MainActivity"
        android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

運行之後,在MainActivity中啓動SecondActivity,然後在SecondActivity中啓動MainActivity,此時我們再看棧內activity的情況:
在這裏插入圖片描述

可以看到此時棧內有3個activity,分別是兩個MainActivity和一個SecondActivity,由此說明singleTop只是在該activity在棧頂時才能複用。
  • singleTask模式:棧內複用模式。這是一種單實例模式,這種模式下,只要是棧內存在該activity實例,多次啓動後不會創建新的實例,而是會複用該activity,並且調用onNewIntent方法。例如:Activity A 是singleTask模式啓動的,再次啓動時,系統會先在棧內找是否存在A的實例的任務棧,如果不存在,則會創建新的任務棧,並將A的實例放在棧中,如果存在A所需的任務棧,則需要看該棧中是否存在A的實例,如果存在則直接調用onNewIntent方法,如果不存在,則創建A的實例。
    還是上述兩個activity,但是我們修改一下MainActivity的啓動模式,並且操作步驟還是上述步驟,我們看一下棧內activity的情況:
    在這裏插入圖片描述

      此時我們可以看到棧內只有一個activity,這就是singleTask的另一個特性,就是說如果singleTask模式的activity在棧內存在,並且在它之上還有別的activity,則再次啓動這個activity的時候,在它之上的所有activity全部出棧,然後直到該activity位於棧頂截止。
    
  • singleInstance模式:但是蘿莉模式。這是一種加強的singleTeask模式。此模式的activity只會單獨存在以一個任務棧中。例如:activity A 是以singleInstance模式,當A 啓動後,系統會爲它創建一個單獨的任務棧,並將A放入棧中,後續的請求不會再創建新的A,除非這個任務棧被銷燬。
    我們將上述MainActivity的啓動模式改爲singleInstance,然後由MainActivity啓動SecondActivity,然後由SecondActivity啓動MainActivity,此時查看棧內情況如下:

在這裏插入圖片描述
我們會發現一個問題,棧內現在只有兩個activity實例,而SecondActivity啓動模式爲standard,照理說,應該存在多個activity的,但是爲什麼只有兩個呢?我們來看一下生命週期日誌:

2019-02-17 00:23:34.490 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onPause(MainActivity.kt:42)
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onPause
2019-02-17 00:23:34.491 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.539 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onRestart(SecondActivity.kt:57)
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onRestart
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.540 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onStart(SecondActivity.kt:39)
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onStart
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.541 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ SecondActivity.onResume(SecondActivity.kt:45)
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onResume
2019-02-17 00:23:34.542 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.586 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onSaveInstanceState(MainActivity.kt:84)
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onSaveInstanceState
2019-02-17 00:23:34.587 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➀: ╔══════════════════════════════════════════════════════════════
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➁: ║ MainActivity.onStop(MainActivity.kt:66)
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➂: ╟──────────────────────────────────────────────────────────────
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➃: ║ onStop
2019-02-17 00:23:34.588 18292-18292/com.xyd.activitylaunchmodedemo D/LogUtils➄: ╚══════════════════════════════════════════════════════════════

	由MainActivity再次啓動SecondActivity時,沒有重新創建activity,只是重新顯示出來,調用了onRestart方法。這個問題後續研究。

未完待續。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章