Android Activity 啟動模式的功能驗證
之前一直都是看別人寫的啟動模式,發現網上大多數的內容都是抄襲來抄襲去,直到最近看了開發藝術這本書,發現之前對啟動模式的理解過於簡單,很多東西都沒有考慮到,為了加深理解,於是決定自己動手去驗證一下四個啟動模式。當然我們也從最簡單的啟動模式開始驗證。
為了列印方便,定義一個基礎Activity,在其onCreate方法和onNewIntent方法中列印出當前Activity的日誌資訊,主要包括所屬的task,當前類的hashcode,以及taskAffinity的值。之後我們進行測試的Activity都直接繼承該Activity
public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("TAG", "===========================================onCreate========================================================="); Log.e("TAG", "onCreate " + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode()); dumpTaskAffinity(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.e("TAG", "===========================================onNewIntent========================================================="); Log.e("TAG", "onNewIntent " + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode()); dumpTaskAffinity(); } protected void dumpTaskAffinity(){ try { ActivityInfo info = this.getPackageManager() .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Log.e("TAG", "taskAffinity:"+info.taskAffinity); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } }
standard模式
這個模式是預設的啟動模式,即標準模式,在不指定啟動模式的前提下,系統預設使用該模式啟動Activity,每次啟動一個Activity都會重寫建立一個新的例項,不管這個例項存不存在,這種模式下,誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中。
新建一個Activity,並宣告在manifest檔案中
<activity android:name=".standard.StandardActivity" android:launchMode="standard" > </activity>
對於standard模式,android:launchMode可以不進行宣告,因為預設就是standard。
StandardActivity 的程式碼如下,入口Activity中有一個按鈕進入該Activity,這個Activity中又有一個按鈕啟動StandardActivity。
public class StandardActivity extends BaseActivity { private Button jump; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_standard); jump= (Button) findViewById(R.id.jump); jump.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(StandardActivity.this, StandardActivity.class); startActivity(intent); } }); } }
看下gif動態圖,有點卡頓,湊合看,我們首先進入StandardActivity,進入後再點選**測試Standard**的按鈕,再按四次返回鍵不斷返回。
輸出的日誌如下
可以看到日誌輸出了四次StandardActivity的和一次MainActivity的,從MainActivity進入StandardActivity一次,後來我們又按了三次按鈕,總共四次StandardActivity的日誌,並且所屬的任務棧的id都是62,這也驗證了**誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中**這句話,因為啟動StandardActivity的是MainActivity,而MainActivity的taskId是62,因此啟動的StandardActivity也應該屬於id為62的這個task,後續的3個StandardActivity是被StandardActivity這個物件啟動的,因此也應該還是62,所以taskId都是62。並且每一個Activity的hashcode都是不一樣的,說明他們是不同的例項,即**每次啟動一個Activity都會重寫建立一個新的例項**
singleTop,棧頂複用模式
這個模式下,如果新的activity已經位於棧頂,那麼這個Activity不會被重寫建立,同時它的onNewIntent方法會被呼叫。如果棧頂不存在該Activity的例項,則情況與standard模式相同。
SingleTopActivity程式碼和StandardActivity類似,只不過記得在manifest文字後中修改啟動模式。
<activity android:name=".singletop.SingleTopActivity" android:launchMode="singleTop">
操作和standard模式類似,直接貼輸出日誌
我們看到,除了第一次進入SingleTopActivity這個Activity時,輸出的是onCreate方法中的日誌,後續的都是呼叫了onNewIntent方法,並沒有呼叫onCreate方法,並且四個日誌的hashcode都是一樣的,說明棧中只有一個例項。這是因為第一次進入的時候,棧中沒有該例項,則建立,後續的三次發現棧頂有這個例項,則直接複用,並且呼叫onNewIntent方法。那麼假設棧中有該例項,但是該例項不在棧頂情況又如何呢。
我們先從MainActivity中進入到SingleTopActivity,然後再跳轉到OtherActivity中,再從OtherActivity中跳回SingleTopActivity,再從SingleTopActivity跳到SingleTopActivity中,看看整個過程的日誌。
我們看到從MainActivity進入到SingleTopActivity時,新建了一個SingleTopActivity物件,並且task id與MainActivity是一樣的,然後從SingleTopActivity跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到SingleTopActivity,即使棧中已經有SingleTopActivity例項了,但是依然會建立一個新的SingleTopActivity例項,這一點從上面的日誌的hashCode可以看出,此時棧頂是SingleTopActivity,如果再跳到SingleTopActivity,就會複用棧頂的SingleTopActivity,即會呼叫SingleTopActivity的onNewIntent方法。這就是上述日誌的全過程。
對以上內容進行總結
- standard啟動模式是預設的啟動模式,每次啟動一個Activity都會新建一個例項不管棧中是否已有該Activity的例項。
- singleTop模式分3種情況
- 噹噹前棧中已有該Activity的例項並且該例項位於棧頂時,不會新建例項,而是複用棧頂的例項,並且會將Intent物件傳入,回撥onNewIntent方法。
- 噹噹前棧中已有該Activity的例項但是該例項不在棧頂時,其行為和standard啟動模式一樣,依然會建立一個新的例項
- 噹噹前棧中不存在該Activity的例項時,其行為同standard啟動模式。
- standard和singleTop啟動模式都是在原任務棧中新建Activity例項,不會啟動新的Task,即時你指定了taskAffinity屬性。
那麼什麼是taskAffinity屬性呢,可以簡單的理解為任務相關性。
- 這個引數標識了一個Activity所需任務棧的名字,預設情況下,所有Activity所需的任務棧的名字為應用的包名。
- 我們可以單獨指定每一個Activity的taskAffinity屬性覆蓋預設值
- 一個任務的affinity決定於這個任務的根activity(root activity)的taskAffinity。
- 在概念上,具有相同的affinity的activity(即設定了相同taskAffinity屬性的activity)屬於同一個任務。
- 為一個activity的taskAffinity設定一個空字串,表明這個activity不屬於任何task。
很重要的一點taskAffinity屬性不對standard和singleTop模式有任何影響,即時你指定了該屬性為其他不同的值,這兩種啟動模式下不會建立新的task
我們指定之前的例子的taskAffinity分別為其他不同的值,入口Activity不指定(不指定即預設值,即包名)
<activity android:name=".standard.StandardActivity" android:launchMode="standard" android:taskAffinity="cn.edu.zafu.lifecycle.standard" > </activity> <activity android:name=".singletop.SingleTopActivity" android:launchMode="singleTop" android:taskAffinity="cn.edu.zafu.lifecycle.singletop" > </activity>
分別啟動這兩個Activity看日誌輸出
我們看到入口Activity的taskAffinity值就是包名,就是預設情況下不指定的。然後StandardActivity和SingleTopActivity的taskAffinity值被我們覆蓋了,分別為不同的值,但是這兩個Activity啟動的時候任務棧並沒有新建,而是直接在原來的Task中啟動,這說明這個taskAffinity對這兩種啟動模式沒有什麼影響。其實該屬性主要是配合SingleTask啟動模式使用的。接下來我們看該啟動模式,可以說這個啟動模式是最複雜的。
singleTask,即棧內複用模式
這個模式十分複雜,有各式各樣的組合。在這個模式下,如果棧中存在這個Activity的例項就會複用這個Activity,不管它是否位於棧頂,複用時,會將它上面的Activity全部出棧,並且會回撥該例項的onNewIntent方法。其實這個過程還存在一個任務棧的匹配,因為這個模式啟動時,會在自己需要的任務棧中尋找例項,這個任務棧就是通過taskAffinity屬性指定。
如果這個任務棧不存在,則會建立這個任務棧。
指定Activity為singleTask模式
<activity android:name=".singleTask.SingleTaskActivity" android:launchMode="singleTask" > </activity>
現在我們不指定任何taskAffinity屬性,對它做類似singleTop的操作,即從入口MainActivity進入SingleTaskActivity,然後跳到OtherActivity,再跳回到SingleTaskActivity。看看整個過程的日誌。
當我們從MainActiviyty進入到SingleTaskActivity,再進入到OtherActivity後,此時棧中有3個Activity例項,並且SingleTaskActivity不在棧頂,而在OtherActivity跳到SingleTaskActivity時,並沒有建立一個新的SingleTaskActivity,而是複用了該例項,並且回撥了onNewIntent方法。並且原來的OtherActivity出棧了,具體見下面的資訊,使用命令adb shell dumpsys activity activities可進行檢視
Running activities (most recent first):
TaskRecord{434c6d90 #77 A=cn.edu.zafu.lifecycle U=0 sz=2}
Run #2: ActivityRecord{4358c850 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t77}
Run #1: ActivityRecord{43576720 u0 cn.edu.zafu.lifecycle/.MainActivity t77}
可以看到當前棧中只有兩個Activity,即原來棧中位於SingleTaskActivity 之上的Activity都出棧了。
我們看到使用singleTask啟動模式啟動一個Activity,它還是在原來的task中啟動。其實是這樣的,我們並沒有指定taskAffinity屬性,這說明和預設值一樣,也就是包名,當MainActivity啟動時建立的Task的名字就是包名,因為MainActivity也沒有指定taskAffinity,而當我們啟動SingleTaskActivity ,首先會尋找需要的任務棧是否存在,也就是taskAffinity指定的值,這裡就是包名,發現存在,就不再建立新的task,而是直接使用。當該task中存在該Activity例項時就會複用該例項,這就是棧內複用模式。
這時候,如果我們指定SingleTaskActivity 的taskAffinity值。
<activity android:name=".singleTask.SingleTaskActivity" android:launchMode="singleTask" android:taskAffinity="cn.edu.zafu.lifecycle.singleTask" > </activity>
還是之前的操作。但是日誌就會變得不一樣。
我們看到SingleTaskActivity所屬的任務棧的TaskId發生了變換,也就是說開啟了一個新的Task,並且之後的OtherActivity也執行在了該Task上
列印出資訊也證明了存在兩個不同的Task
Running activities (most recent first):
TaskRecord{441ab678 #79 A=cn.edu.zafu.lifecycle.singleTask U=0 sz=1}
Run #3: ActivityRecord{432fa178 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t79}
TaskRecord{43ccd2a8 #78 A=cn.edu.zafu.lifecycle U=0 sz=1}
Run #2: ActivityRecord{42d5e7a8 u0 cn.edu.zafu.lifecycle/.MainActivity t78}
如果我們指定MainActivity的taskAffinity屬性和SingleTaskActivity一樣,又會出現什麼情況呢。
沒錯,就是和他們什麼都不指定是一樣的。
這時候,就有了下面的結論
- singleTask啟動模式啟動Activity時,首先會根據taskAffinity去尋找當前是否存在一個對應名字的任務棧
- 如果不存在,則會建立一個新的Task,並建立新的Activity例項入棧到新建立的Task中去
- 如果存在,則得到該任務棧,查詢該任務棧中是否存在該Activity例項
- 如果存在例項,則將它上面的Activity例項都出棧,然後回撥啟動的Activity例項的onNewIntent方法
- 如果不存在該例項,則新建Activity,併入棧
此外,我們可以將兩個不同App中的Activity設定為相同的taskAffinity,這樣雖然在不同的應用中,但是Activity會被分配到同一個Task中去
我們再建立另外一個應用,指定它的taskAffinity和之前的一樣,都是cn.edu.zafu.lifecycle.singleTask
<activity android:name=".OtherActivity" android:launchMode="singleTask" android:taskAffinity="cn.edu.zafu.lifecycle.singleTask" > </activity>
然後啟動一個應用,讓他跳轉到該Activity後,再按home鍵後臺,啟動另一個應用再進入該Activity,看日誌
我們看到,指定了相同的taskAffinity的SingleTaskActivity和OtherActivity被啟動到了同一個task中,taskId都為274。
singleInstance模式
該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全域性唯一性,即整個系統中就這麼一個例項。以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個例項,那麼會把它所在的任務排程到前臺,重用這個例項。
增加一個Activity,宣告如下
<activity android:name=".singleinstance.SingleInstanceActivity" android:launchMode="singleInstance" > <intent-filter> <action android:name="cn.edu.zafu.lifecycle"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
使用下面的方式分別在兩個應用中啟動它
Intent intent = new Intent(); intent.setAction("cn.edu.zafu.lifecycle"); startActivity(intent);
做的操作和上一次是一樣的,檢視日誌
我們看到,第一個應用啟動SingleInstanceActivity時,由於系統中不存在該例項,所以新建了一個Task,按home鍵後,使用另一個App進入該Activity,由於系統中已經存在了一個例項,不會再建立新的Task,直接複用該例項,並且回撥onNewIntent方法。可以從他們的hashcode中可以看出這是同一個例項。
對以上內容的總結就是
SingleInstance模式啟動的Activity在系統中具有全域性唯一性。
相關文章
- Android-Activity的啟動模式Android模式
- Android學習筆記-Activity的啟動模式Android筆記模式
- 擼擼Android的羊毛(一)----Activity啟動模式Android模式
- Activity啟動模式模式
- Activity啟動模式(GIF 動態演示)模式
- Activity 的 "啟動流程"(基於 Android 9.0)Android
- Android中Activity的四種啟動方式Android
- Android Activity啟動流程原始碼分析Android原始碼
- Android程式啟動與Activity顯示Android
- Android原始碼分析:Activity啟動流程Android原始碼
- Activity生命週期與啟動模式模式
- Android Activity是如何啟動的?Activity的生命週期是如何呼叫的?Android
- 組內技術分享-Activity 的啟動模式模式
- 【筆記】【Android】Activity的Task模式筆記Android模式
- 深入理解Android 之 Activity啟動流程(Android 10)Android
- Android 8.0 原始碼分析 (四) Activity 啟動Android原始碼
- Android黑科技:如何啟動未註冊的ActivityAndroid
- Android啟動模式Android模式
- 重溫Android四大元件(二)—Activity的啟動模式與標誌位Android元件模式
- Activity的啟動模式及IntentFilter匹配規則總結模式IntentFilter
- ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解模式
- 一篇文章搞懂 Activity 啟動模式模式
- [Android]關閉所有Activity,開啟某個ActivityAndroid
- 原始碼閱讀之Activity啟動與App啟動流程 – Android 9.0原始碼APPAndroid
- 原始碼閱讀之Activity啟動與App啟動流程 - Android 9.0原始碼APPAndroid
- Android系統原始碼分析--Activity啟動過程Android原始碼
- Android入門教程之Activity(生命週期,啟動...)Android
- 理解Android的四種啟動模式Android模式
- Android四種啟動模式Android模式
- Android Activity Deeplink啟動來源獲取原始碼分析Android原始碼
- Activity啟動流程分析
- Android Home鍵之後啟動Activity延遲5sAndroid
- Android之Activity啟動流程詳解(基於api28)AndroidAPI
- 從Activity的啟動流程理解Binder
- 根Activity元件的啟動過程元件
- App 竟然是這樣跑起來的 —— Android App/Activity 啟動流程分析APPAndroid
- Activity啟動模式聯想到多程式相關的一些東西模式
- [譯]Android系統是如何啟動應用程式的?從Zygote到Activity的onCreate()AndroidGo
- 應用被其他app開啟時,不同啟動模式下activity任務棧的變化APP模式