在上一篇文章《安卓開發筆記——重識Activity》中,我們了解了Activity生命周期的執行順序和一些基本的數據保存操作,但如果只知道這些是對于我們的開發需求來說是遠遠不夠的,今天我們繼續探索Activity,來了解下關于Activity任務棧和Activity四種啟動模式的區別。
為什么需要了解關于Activity的任務棧,其實最直接的體現就是提高用戶交互友好性。
舉個例子,當我們去瀏覽一個新聞客戶端的時候,我們進入了新聞詳情頁,在這個頁面有相隔兩條的新聞標題,當我們去點擊這個標題的時候進入了新的新聞詳情頁時,如果我們不加以控制會導致什么現象?它會創建出n個新聞詳細頁的Activity實例,導致用戶在退出的時候需要推出多個新聞詳情activity,這點在用戶體驗上是非常不好的,當然對于我們自身的程序也是非常不好的,不斷的去創建新的Activity必定會消耗一定的內存,久而久之,應用程序會越來越卡甚至崩潰。在我之前寫過的博文《安卓開發筆記——打造屬于自己的博客園APP(四)》中也應用到了這一知識點。
在講Activity任務棧前,我們應該先知道什么是棧?
簡單點來理解,可以把棧比作一個開封的箱子,我們可以往里面塞東西,這里假設塞的東西的底面積和箱子的底面積是相同的,那么這些東西就具備有從下往上一定的順序,當我們想要取出箱子里面的東西時,我們沒有辦法一下子拿到箱子最底層的東西,我們只能拿到最上面一層的東西,從上往下。
來看下這張圖,這里的箱子就是棧,箱子口可以看作是棧的入口與出口,東西代表數據。棧的特點:具有一定的次序,后進先出(越先放入的東西,越晚出來)。
1、Activity任務棧
好了,在了解了什么是棧之后,我們可以開始進入今天的主題了,在Android的官方文檔描述中我們可以知道,任務棧也是棧,具有棧的一切特點。
Activity任務棧,顧名思義是存放Activity任務的棧,這里的任務棧為上圖箱子,Activity為上圖的東西。
當我們每打開一個Activity的時候它會就往Activity任務棧中壓入一個Activity,當我們每銷毀一個Activity的時候它會從Activity任務棧中彈出一個Activity,由于安卓系統自身的設計,我們只能在手機屏幕上獲取當前一個Activity的焦點即棧頂元素(最上面的Activity),其余的Activity會暫居后臺等待系統調用。
1.1、關于任務棧的概念:
1.2、關于任務棧的缺點:
2、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 @Override17 protected void onCreate(Bundle savedInstanceState) {18 super.onCreate(savedInstanceState);19 setContentView(R.layout.activity_main);20 21 Log.i(TAG,"第一個Activity,加入任務棧:"+getTaskId());22 23 //點擊按鈕跳轉第二個Activity24 mbButton = (Button) findViewById(R.id.bt_button);25 mbButton.setOnClickListener(new View.OnClickListener() {26 @Override27 public void onClick(View v) {28 startActivity(new Intent(MainActivity.this, SecondActivity.class));29 }30 });31 32 33 }34 35 @Override36 protected void onDestroy() {37 super.onDestroy();38 Log.i(TAG, "第一個Activity,退出任務棧:" + getTaskId());39 }40 }MainActivity.java
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 @Override17 protected void onCreate(Bundle savedInstanceState) {18 super.onCreate(savedInstanceState);19 setContentView(R.layout.activity_second);20 Log.i(TAG, "第二個Activity,加入任務棧:" + getTaskId());21 //點擊按鈕跳轉第二個Activity22 mbButton1 = (Button) findViewById(R.id.bt_button1);23 mbButton1.setOnClickListener(new View.OnClickListener() {24 @Override25 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 @Override32 public void onClick(View v) {33 startActivity(new Intent(SecondActivity.this, SecondActivity.class));34 }35 });36 }37 38 @Override39 protected void onDestroy() {40 super.onDestroy();41 Log.i(TAG, "第二個Activity,退出任務棧:" + getTaskId());42 }43 }SecondActivity.java
實驗一:啟動模式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 <activity10 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 <activity10 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 <activity10 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 <activity10 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 @Override2 protected void onNewIntent(Intent intent) {3 super.onNewIntent(intent);4 Log.i(TAG, "第二個Activity,執行onNewIntent");5 }
1、在standard(默認)啟動模式下,我們來回的去跳轉Activity,看下日志打印,發現是不會調用onNewIntent方法的,因為它不是一個實例。
2、在singleTop模式下,我們從第一個Activity跳轉到第二個Activity,再從第二個Activity跳轉自身,再跳轉第一個Activity,看下日志打印,我們可以發現,當第二個Activity置于棧頂的時候,由于重用了實例,所以調用了onNewIntent方法。
3、當singleTask和singleInstance模式下也是一樣的,因為重用了實例,所以會調用onNewIntent方法,且onNewIntent方法是在前一個Activity的onStop方法后(當前ActivityonReStart方法前)立即調用的。
好了,今天先寫到這里,有什么建議或疑問,可以在文章評論給我留言。
作者:李晨瑋出處:http://www.companysz.com/lichenwei/本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我!
|
新聞熱點
疑難解答