Android中Activity的啟動模式(LaunchMode)和使用場景
轉載請註明出處:http://www.jianshu.com/p/c4cba9c94fa8
本文出自Shawpoo的簡書
我的部落格:CSDN部落格
一、為什麼需要啟動模式
在Android開發中,我們都知道,在預設的情況下,如果我們啟動的是同一個Activity的話,系統會建立多個例項並把它們一一放入任務棧中。當我們點選返回(back)鍵,這些Activity例項又將從任務棧中一一移除,遵循的原則是“後進先出”(先進後出)。
這裡我們考慮一個問題,當我們多次啟動同一個Activity,系統也會建立多個例項放入任務棧中,這樣豈不是很耗費記憶體資源?為了解決這一問題,Android為Actiivty提供了啟動模式。
Activity的啟動模式有四種:standard、singleTop、singleTask和singleInstance。
二、啟動模式的分類
1、standard:標準模式
這種啟動模式為標準模式,也是預設模式。每當我們啟動一個Activity,系統就會相應的建立一個例項,不管這個例項是否已經存在。這種模式,一個棧中可以有多個例項,每個例項也都有自己的任務棧。而且是誰啟動了此Activity,那麼這個Activity就執行在啟動它的Activity所在的棧中。
- Manifest中配置:
對於標準模式,android:launchMode="standard"可以不寫,因為預設就是standard模式。
<activity
android:name=".StandardActivity"
android:launchMode="standard" >
</activity>
- 使用案例:
MainActivity有一個按鈕,點選按鈕會開啟StandardActivity。開啟StandardActivity也有一個按鈕,點選也是啟動一個StandardActivity。並且我們在onCreate()方法中列印TaskId和hashCode值。
開啟步驟:MainActivity->StandardActivity->StandardActivity->StandardActivity
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_demo);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StandardActivity.open(MainActivity.this);
}
});
Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
}
StandardActivity :
/**
* 啟動模式:Standard(標準模式)
*/
public class StandardActivity extends AppCompatActivity {
private static final String TAG = StandardActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch_mode);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
open(StandardActivity.this);
}
});
Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
public static void open(Context context) {
context.startActivity(new Intent(context, StandardActivity.class));
}
}
控制檯列印log如下:
通過案例的log分析,可以得出標準模式下,每當開啟一次Activity就會建立一個新的例項,因為hashCode值都不同,而且都建立在啟動它的Activity所屬的任務棧中,也就是MainActivity所在的任務棧中,因為它們的任務棧Id一致。
- 分析總結:
標準模式下,只要啟動一次Activity,系統就會在當前任務棧新建一個例項。
- 使用場景:
正常的去開啟一個新的頁面,這種啟動模式使用最多,最普通 。
2、singleTop:棧頂複用模式
這種啟動模式下,如果要啟動的Activity已經處於棧的頂部,那麼此時系統不會建立新的例項,而是直接開啟此頁面,同時它的onNewIntent()方法會被執行,我們可以通過Intent進行傳值,而且它的onCreate(),onStart()方法不會被呼叫,因為它並沒有發生任何變化。
- Manifest中配置:
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop">
</activity>
- 使用案例:
MainActivity仍然是一個按鈕,點選按鈕開啟SingleTopActivity,SingleTopActivity有兩個按鈕,一個是開啟SingleTopActivity,一個是開啟OtherActivity,OtherActivity有一個按鈕,點選按鈕可以開啟SingleTopActivity。而且我們在onCreate()、onNewIntent()列印taskId和hashCode值。
開啟步驟:MainActivity->SingleTopActivity->SingleTopActivity->OtherActivity->SingleTopActivity->SingleTopActivity
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_demo);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SingleTopActivity.open(MainActivity.this);
}
});
Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
SingleTopActivity :
/**
* 啟動模式:棧頂複用模式
*/
public class SingleTopActivity extends AppCompatActivity {
private static final String TAG = SingleTopActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch_mode);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
open(SingleTopActivity.this);
}
});
findViewById(R.id.btn_other).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
OtherActivity.open(SingleTopActivity.this);
}
});
Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e(TAG, "———onNewIntent(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
public static void open(Context context) {
context.startActivity(new Intent(context, SingleTopActivity.class));
}
}
OtherActivity:
public class OtherActivity extends AppCompatActivity {
private static final String TAG = OtherActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
findViewById(R.id.btn_singleTop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SingleTopActivity.open(OtherActivity.this);
}
});
Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
}
public static void open(Context context) {
context.startActivity(new Intent(context, OtherActivity.class));
}
}
控制檯列印log如下:
首先,因為它們的taskId值都相同,所以它們同屬於一個任務棧。其次,第一次啟動SingleTopActivity的時候會執行onCreate()方法新建一個例項,然後再次啟動SingleTopActivity頁面會回撥onNewIntent(),說明沒有建立新的例項,而且hashCode值沒有發生改變。此時我們繼續開啟另一個Activity,這時OtherActivity處於棧頂,我們繼續啟動SingleTopActivity,這時發現又是執行了onCreate(),說明又重新建立了新的例項,當我們繼續啟動SingleTopActivity,發現回撥了onNewIntent(),同樣hashCode值沒有發生改變,證明沒有重新建立例項。
- 分析總結:
通過上述案例歸納為以下三點:
1、當前棧中已有該Activity的例項並且該例項位於棧頂時,不會建立例項,而是複用棧頂的例項,並且會將Intent物件傳入,回撥onNewInten()方法;
2、當前棧中已有該Activity的例項但是該例項不在棧頂時,其行為和standard啟動模式一樣,依然會建立一個新的例項;
3、當前棧中不存在該Activity的例項時,其行為同standard啟動模式。
- 使用場景:
這種模式應用場景的話,假如一個新聞客戶端,在通知欄收到了3條推送,點選每一條推送會開啟新聞的詳情頁,如果為預設的啟動模式的話,點選一次開啟一個頁面,會開啟三個詳情頁,這肯定是不合理的。如果啟動模式設定為singleTop,當點選第一條推送後,新聞詳情頁已經處於棧頂,當我們第二條和第三條推送的時候,只需要通過Intent傳入相應的內容即可,並不會重新開啟新的頁面,這樣就可以避免重複開啟頁面了。
3、singleTask:站內複用模式
在這個模式下,如果棧中存在這個Activity的例項就會複用這個Activity,不管它是否位於棧頂,複用時,會將它上面的Activity全部出棧,因為singleTask本身自帶clearTop這種功能。並且會回撥該例項的onNewIntent()方法。其實這個過程還存在一個任務棧的匹配,因為這個模式啟動時,會在自己需要的任務棧中尋找例項,這個任務棧就是通過taskAffinity屬性指定。如果這個任務棧不存在,則會建立這個任務棧。不設定taskAffinity屬性的話,預設為應用的包名。
- 使用案例:
MainActivity仍然是一個按鈕,點選按鈕開啟SingleTaskActivity,SingleTaskActivity有兩個按鈕,一個是開啟SingleTaskActivity,另一個同樣是開啟OtherActivity,OtherActivity有一個按鈕,點選按鈕可以開啟SingleTaskActivity。同樣我們在onCreate()、onNewIntent()列印taskId和hashCode值。
開啟步驟:MainActivity->SingleTaskActivity->SingleTaskActivity->OtherActivity->SingleTaskActivity->SingleTaskActivity
程式碼和SingleTop基本一樣,只有Manifest中配置不同,這裡不再贅述。
1、不設定taskAffinity屬性,也就是默在同一個任務棧中。
Manifest中配置:
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask">
</activity>
控制檯列印log如下:
首先,因為發現它們的taskId值都相同,所以它們同屬於一個任務棧。其次,第一次啟動SingleTaskActivity的時候會執行onCreate()方法新建一個例項,然後再次啟動SingleTaskActivity頁面會回撥onNewIntent(),說明沒有建立新的例項,而且hashCode值沒有發生改變。此時我們繼續開啟另一個Activity,然後繼續啟動SingleTaskActivity,這時發現仍然只回撥onNewIntent(),說明沒有建立新的例項,當我們繼續啟動SingleTaskActivity,仍然只是回撥了onNewIntent(),此過程中發現hashCode值始終沒有發生改變,證明引用都是同一個的例項。
2、設定taskAffinity屬性,singleTask所在的Activity與啟動它的Activity處於不同的任務棧中。
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask"
android:taskAffinity="${applicationId}.singleTask">
</activity>
指定了taskAffinity後,我們發現除了taskId有區別外,其他呼叫基本沒有什麼區別。因為MainActivity沒有指定taskAffinity屬性,預設為包名,與SingleTaskActivity不同,所以在啟動SingleTaskActivity時,發現這個棧不存在,系統首先會建立這個棧然後將SingleTaskActivity壓入棧中。之後我們發現只要棧中存在SingleTaskActivity這個例項,就會直接引用。
3、通過adb shell dumpsys activity activities檢視當前執行的Activity
執行完上面的步驟,我們通過上面的資訊得出,發現只有MainActivity和SingleTaskActivity在執行,而且也可以看出確實有1909和1910兩個任務棧。那OtherActivity哪去了?那是因為SingleTaskActivity具有ClearTop的功能,當複用SingleTashActivity的時候會將棧中SingleTaskActivity之上的Activity全部清掉,所以OtherActivity其實是被銷燬了。
- 分析總結:
在複用的時候,首先會根據taskAffinity去找對應的任務棧:
1、如果不存在指定的任務棧,系統會新建對應的任務棧,並新建Activity例項壓入棧中。
2、如果存在指定的任務棧,則會查詢該任務棧中是否存在該Activity例項
a、如果不存在該例項,則會在該任務棧中新建Activity例項。
b、如果存在該例項,則會直接引用,並且回撥該例項的onNewIntent()方法。並且任務棧中該例項之上的Activity會被全部銷燬。
- 使用場景:
SingleTask這種啟動模式最常使用的就是一個APP的首頁,因為一般為一個APP的第一個頁面,且長時間保留在棧中,所以最適合設定singleTask啟動模式來複用。
4、singleInstance:單例項模式
單例項模式,顧名思義,只有一個例項。該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全域性唯一性,即整個系統中就這麼一個例項,由於棧內複用的特性,後續的請求均不會建立新的Activity例項,除非這個特殊的任務棧被銷燬了。以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個例項,那麼會把它所在的任務排程到前臺,重用這個例項。
- Manifest中配置:
<activity
android:name=".SingleInstanceActivity"
android:launchMode="singleInstance">
</activity>
- 使用案例:
使用上面同樣的程式碼進行測試:
通過測試發現,在第一次開啟SingleInstanceActivity的時候,由於系統不存在該例項,所以系統會新建一個任務棧來存放該Activity例項,而且只要開啟過一次該Activity,後面無論什麼時候再次啟動該Activity,都會直接引用第一次建立的例項,而且會回撥該例項的onNewIntent()方法。
- 分析總結:
啟動該模式Activity的時候,會查詢系統中是否存在:
1、不存在,首先會新建一個任務棧,其次建立該Activity例項。
2、存在,則會直接引用該例項,並且回撥onNewIntent()方法。
特殊情況:該任務棧或該例項被銷燬,系統會重新建立。
- 使用場景:
很常見的是,電話撥號盤頁面,通過自己的應用或者其他應用開啟撥打電話頁面 ,只要系統的棧中存在該例項,那麼就會直接呼叫。
三、總結
在使用APP過程中,不可避免頁面之間的跳轉,那麼就會涉及到啟動模式。其實在對介面進行跳轉時,Android系統既能在同一個任務中對Activity進行排程,也能以Task(任務棧)為單位進行整體排程。在啟動模式為standard或singleTop時,一般是在同一個任務中對Activity進行排程,而在啟動模式為singleTask或singleInstance是,一般會對Task進行整體排程。
相關文章
- Android-Activity的啟動模式Android模式
- Android學習筆記-Activity的啟動模式Android筆記模式
- 擼擼Android的羊毛(一)----Activity啟動模式Android模式
- Android中Activity的四種啟動方式Android
- Activity啟動模式模式
- 喜聞樂見之Activity的launchMode
- Android5.0Activity的轉場動畫和共享元素Android動畫
- Activity啟動模式(GIF 動態演示)模式
- Activity 的 "啟動流程"(基於 Android 9.0)Android
- Android Activity啟動流程原始碼分析Android原始碼
- Android程式啟動與Activity顯示Android
- Android原始碼分析:Activity啟動流程Android原始碼
- Android設計模式——策略模式之原始碼使用場景(三)Android設計模式原始碼
- Activity生命週期與啟動模式模式
- Android Activity是如何啟動的?Activity的生命週期是如何呼叫的?Android
- 組內技術分享-Activity 的啟動模式模式
- 【筆記】【Android】Activity的Task模式筆記Android模式
- 深入理解Android 之 Activity啟動流程(Android 10)Android
- Android 8.0 原始碼分析 (四) Activity 啟動Android原始碼
- Android黑科技:如何啟動未註冊的ActivityAndroid
- Android Reveal圓形Activity轉場動畫Android動畫
- Android啟動模式Android模式
- 重溫Android四大元件(二)—Activity的啟動模式與標誌位Android元件模式
- Android設計模式之——單例模式之原始碼使用場景(一)Android設計模式單例原始碼
- Activity的啟動模式及IntentFilter匹配規則總結模式IntentFilter
- ActivityRecord、TaskRecord、ActivityStack以及Activity啟動模式詳解模式
- 一篇文章搞懂 Activity 啟動模式模式
- 面試中的 ThreadLocal 原理和使用場景面試thread
- sql中in和exists的原理及使用場景。SQL
- Java中的引用型別和使用場景Java型別
- [Android]關閉所有Activity,開啟某個ActivityAndroid
- 原始碼閱讀之Activity啟動與App啟動流程 – Android 9.0原始碼APPAndroid
- 原始碼閱讀之Activity啟動與App啟動流程 - Android 9.0原始碼APPAndroid
- 報表工具中動態引數的使用方法和場景
- Android系統原始碼分析--Activity啟動過程Android原始碼
- Android入門教程之Activity(生命週期,啟動...)Android
- Android開發教程 - 使用Data Binding(三)在Activity中的使用Android
- 理解Android的四種啟動模式Android模式
- Android四種啟動模式Android模式