爲何須要了解關於Activity的任務棧,其實最直接的體現就是提升用戶交互友好性。android
舉個例子,當咱們去瀏覽一個新聞客戶端的時候,咱們進入了新聞詳情頁,在這個頁面有相隔兩條的新聞標題,當咱們去點擊這個標題的時候進入了新的新聞詳情頁時,若是咱們不加以控制會致使什麼現象?它會建立出n個新聞詳細頁的Activity實例,致使用戶在退出的時候須要推出多個新聞詳情activity,這點在用戶體驗上是很是很差的,固然對於咱們自身的程序也是很是很差的,不斷的去建立新的Activity一定會消耗必定的內存,長此以往,應用程序會愈來愈卡甚至崩潰。
app
在講Activity任務棧前,咱們應該先知道什麼是棧?ide
簡單點來理解,能夠把棧比做一個開封的箱子,咱們能夠往裏面塞東西,這裏假設塞的東西的底面積和箱子的底面積是相同的,那麼這些東西就具有有從下往上必定的順序,當咱們想要取出箱子裏面的東西時,咱們沒有辦法一會兒拿到箱子最底層的東西,咱們只能拿到最上面一層的東西,從上往下。this
來看下這張圖,這裏的箱子就是棧,箱子口能夠看做是棧的入口與出口,東西表明數據。棧的特色:具備必定的次序,後進先出(越先放入的東西,越晚出來)。spa
一、Activity任務棧 設計
好了,在瞭解了什麼是棧以後,咱們能夠開始進入今天的主題了,在Android的官方文檔描述中咱們能夠知道,任務棧也是棧,具備棧的一切特色。3d
Activity任務棧,顧名思義是存放Activity任務的棧,這裏的任務棧爲上圖箱子,Activity爲上圖的東西。日誌
當咱們每打開一個Activity的時候它會就往Activity任務棧中壓入一個Activity,當咱們每銷燬一個Activity的時候它會從Activity任務棧中彈出一個Activity,因爲安卓系統自身的設計,咱們只能在手機屏幕上獲取當前一個Activity的焦點即棧頂元素(最上面的Activity),其他的Activity會暫居後臺等待系統調用。code
1.一、關於任務棧的概念:xml
1.二、關於任務棧的缺點:
二、Activity的4種啓動方式
爲了解決任務棧產生的問題,Android爲Activity設計了啓動模式,那麼下面的內容將介紹Android中Activity的啓動模式,這也是最重要的內容之一。
啓動模式(launchMode)在多個Activity跳轉的過程當中扮演着重要的角色,它能夠解決是否生成新的Activity實例,是否重用已經存在的Activity實例,是否和其餘實例共用一個任務棧。任務棧是一個具備棧結構的對象,一個任務棧能夠管理多個Activity,每啓動一個應用,也就建立一個與之對應的任務棧。
<activity>
的android:launchMode屬性爲以上四種之一便可。
1 package com.lcw.rabbit.activitydemo; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 10 public class MainActivity extends Activity { 11 12 private static final String TAG = "Rabbit"; 13 14 private Button mbButton; 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 21 Log.i(TAG,"第一個Activity,加入任務棧:"+getTaskId()); 22 23 //點擊按鈕跳轉第二個Activity 24 mbButton = (Button) findViewById(R.id.bt_button); 25 mbButton.setOnClickListener(new View.OnClickListener() { 26 @Override 27 public void onClick(View v) { 28 startActivity(new Intent(MainActivity.this, SecondActivity.class)); 29 } 30 }); 31 32 33 } 34 35 @Override 36 protected void onDestroy() { 37 super.onDestroy(); 38 Log.i(TAG, "第一個Activity,退出任務棧:" + getTaskId()); 39 } 40 }
1 package com.lcw.rabbit.activitydemo; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 10 public class SecondActivity extends Activity { 11 12 private static final String TAG = "Rabbit"; 13 private Button mbButton1; 14 private Button mbButton2; 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_second); 20 Log.i(TAG, "第二個Activity,加入任務棧:" + getTaskId()); 21 //點擊按鈕跳轉第二個Activity 22 mbButton1 = (Button) findViewById(R.id.bt_button1); 23 mbButton1.setOnClickListener(new View.OnClickListener() { 24 @Override 25 public void onClick(View v) { 26 startActivity(new Intent(SecondActivity.this, MainActivity.class)); 27 } 28 }); 29 mbButton2 = (Button) findViewById(R.id.bt_button2); 30 mbButton2.setOnClickListener(new View.OnClickListener() { 31 @Override 32 public void onClick(View v) { 33 startActivity(new Intent(SecondActivity.this, SecondActivity.class)); 34 } 35 }); 36 } 37 38 @Override 39 protected void onDestroy() { 40 super.onDestroy(); 41 Log.i(TAG, "第二個Activity,退出任務棧:" + getTaskId()); 42 } 43 }
實驗一:啓動模式standard
系統默認的Activity啓動模式是standard,咱們這裏爲了檢驗再設置一下:
1 <activity 2 android:name=".MainActivity" 3 android:launchMode="standard"> 4 <intent-filter> 5 <action android:name="android.intent.action.MAIN" /> 6 <category android:name="android.intent.category.LAUNCHER" /> 7 </intent-filter> 8 </activity> 9 <activity 10 android:name=".SecondActivity" 11 android:launchMode="standard"></activity>
如今咱們進入第一個Activity的時候,點擊按鈕啓動第二個Activity,看下當前任務棧:
當咱們點擊第二個Activity的按鈕,讓它跳轉自身,看下當前任務棧:
當咱們按下Back鍵,跳轉第一個Activity,銷燬第二個Activity時,看下當前任務棧:
能夠發現,這個任務棧和咱們剛上圖描述的箱子(Activity任務棧)是一致的,從上往下放東西(Activity),越晚放進去的東西(Activity)在越上面,然後面的t1072則表明當前任務棧的編號ID,ID相同表明它們屬於同一個任務棧。
咱們從日誌文件中也能夠看得很清楚:
實驗一結論:
在Activity啓動模式爲standard(默認)的狀況下,無論以前有沒有Activity實例,每一次啓動Activity都會建立一個新的Activity實例,並置於Activity任務棧棧頂。
實驗二:啓動模式singleTop
1 <activity 2 android:name=".MainActivity" 3 android:launchMode="standard"> 4 <intent-filter> 5 <action android:name="android.intent.action.MAIN" /> 6 <category android:name="android.intent.category.LAUNCHER" /> 7 </intent-filter> 8 </activity> 9 <activity 10 android:name=".SecondActivity" 11 android:launchMode="singleTop"></activity>
如今咱們進入第一個Activity點擊按鈕跳轉第二個Activity,而後再點擊按鈕跳轉自身,看下當前任務棧:
系統日誌文件:
而後咱們再點擊按鈕啓動第一個Activity,而後點擊按鈕啓動第二個Activity,看下當前任務棧:
系統日誌:
實驗二結論:
在Activity啓動模式爲singleTop(棧頂任務惟一)的狀況下,若是當前Activity處於棧頂,那麼它就不會再去實例化一個新的Activity,當Activity不處於棧頂的時候,會從新實例化一個新的Activity並置於棧頂,此時的任務棧編號爲1080。
實驗三:啓動模式singleTask
1 <activity 2 android:name=".MainActivity" 3 android:launchMode="standard"> 4 <intent-filter> 5 <action android:name="android.intent.action.MAIN" /> 6 <category android:name="android.intent.category.LAUNCHER" /> 7 </intent-filter> 8 </activity> 9 <activity 10 android:name=".SecondActivity" 11 android:launchMode="singleTask"></activity>
當咱們進入第一個Activity點擊進入第二個,再啓動自身,看下當前任務棧:
系統日誌:
如今咱們點擊啓動第一個Activity,再點擊啓動第二個Activity,看下當前任務棧:
系統日誌:
實驗三結論:
在Activity啓動模式爲singleTask(惟一實例)的狀況下,當啓動Activity的時候,若是當前Activity不存在則實例化一個新的Activity,若是當前Activity在任務棧中已經存在,則會複用這個Activity實例,但這邊咱們從日誌打印能夠看出在啓動第二個Activity的時候,第一個Activity推出了任務棧,也就意味着當啓動模式爲singTask的時候,啓動已經存在在Activity任務棧中但不在棧頂的Activity時,該Activity會把壓在它前面的全部Activity彈出任務棧,此時任務棧編號爲1081,屬於同一個任務棧。
實驗四:啓動模式singleInstance
1 <activity 2 android:name=".MainActivity" 3 android:launchMode="standard"> 4 <intent-filter> 5 <action android:name="android.intent.action.MAIN" /> 6 <category android:name="android.intent.category.LAUNCHER" /> 7 </intent-filter> 8 </activity> 9 <activity 10 android:name=".SecondActivity" 11 android:launchMode="singleInstance"></activity>
如今咱們進入第一個Activity點擊按鈕跳轉第二個Activity,再讓其跳轉自身,看下當前任務棧:
系統日誌:
如今點擊按鈕啓動第一個Activity,再點擊按鈕啓動第二個Activity,看下當前任務棧:
系統任務:
點擊Back鍵,直到程序徹底退出,看下系統日誌:
實驗四結論:
在Activity啓動模式爲singleInstance的狀況下,首先咱們能夠發現的是啓動模式爲singleInstance的Activity處於不一樣的任務棧(Task編號不一樣),並保證再也不有其餘的Activity實例進入,它仍是和singleTask同樣保持惟一實例,而後它的退出順序是再也不是根據調用順序,而是在不一樣的任務棧中,從上往下退出。
在共用一個Activity實例時,期間發生了什麼?
在上訴模式中,當咱們的Activity涉及到同一實例的時候,期間Activity作了哪些事情?在Android官方文檔中咱們能夠知道期間雖然沒有新實例化一個Activity,可是調用了onNewIntent方法。
如今咱們在第二個Activity裏添加一個onNewIntent方法:
1 @Override 2 protected void onNewIntent(Intent intent) { 3 super.onNewIntent(intent); 4 Log.i(TAG, "第二個Activity,執行onNewIntent"); 5 }
一、在standard(默認)啓動模式下,咱們來回的去跳轉Activity,看下日誌打印,發現是不會調用onNewIntent方法的,由於它不是一個實例。
二、在singleTop模式下,咱們從第一個Activity跳轉到第二個Activity,再從第二個Activity跳轉自身,再跳轉第一個Activity,看下日誌打印,咱們能夠發現,當第二個Activity置於棧頂的時候,因爲重用了實例,因此調用了onNewIntent方法。
三、當singleTask和singleInstance模式下也是同樣的,由於重用了實例,因此會調用onNewIntent方法,且onNewIntent方法是在前一個Activity的onStop方法後(當前ActivityonReStart方法前)當即調用的。