一、Activity的生命周期
掌握Activity的生命周期對于各任何Android開發(fā)者來說都非常重要。當(dāng)深入理解Activity的生命周期之后,就可以寫出更加連貫流暢的程序。
1、返回棧
通過前面的學(xué)習(xí),我們發(fā)現(xiàn)Android中的Activity是可以層疊的。每新啟動(dòng)一個(gè)Activity,就會(huì)覆蓋原來的Activity,然后點(diǎn)擊back鍵會(huì)銷毀最上面的,下面的一個(gè)Activity就會(huì)重新顯示出來。
Android是使用任務(wù)(Task)來管理Activity的,一個(gè)任務(wù)就是一組存放在棧里的Activity集合,這個(gè)棧也被稱作返回棧(Back Stack)。棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),在默認(rèn)情況下,每當(dāng)我們啟動(dòng)一個(gè)新的Activity,它會(huì)在返回棧中入棧,并處于棧頂。在我們按下back鍵銷毀一個(gè)Activity時(shí),處于棧頂?shù)腁ctivity會(huì)出棧,前一個(gè)入棧的Activity重新回到棧頂。系統(tǒng)總是將棧頂?shù)腁ctivity展示給用戶。
2、Activity狀態(tài)
每個(gè)活動(dòng)在其生命周期中最多可能會(huì)有4種狀態(tài)。
1). 運(yùn)行狀態(tài)
當(dāng)一個(gè)活動(dòng)位于返回棧的棧頂時(shí), 這時(shí)活動(dòng)就處于運(yùn)行狀態(tài)。系統(tǒng)最不愿意回收的就是處于運(yùn)行狀態(tài)的活動(dòng), 因?yàn)檫@會(huì)帶來非常差的用戶體驗(yàn)。
2). 暫停狀態(tài)
當(dāng)一個(gè)活動(dòng)不再處于棧頂位置, 但仍然可見時(shí), 這時(shí)活動(dòng)就進(jìn)人了暫停狀態(tài)。你可能會(huì)覺得既然活動(dòng)已經(jīng)不在棧頂了, 還怎么會(huì)可見呢? 這是因?yàn)椴⒉皇敲恳粋€(gè)活動(dòng)都會(huì)占滿整個(gè)屏幕的,比如對話框形式的活動(dòng)只會(huì)占用屏幕中間的部分區(qū)域, 你很快就會(huì)在后面看到這種活動(dòng)。 處于暫停狀態(tài)的活動(dòng)仍然是完全存活著的, 系統(tǒng)也不愿意去回收這種活動(dòng)〈因?yàn)樗€是可見的, 回收可見的東西都會(huì)在用戶體驗(yàn)方面有不好的影響 ), 只有在內(nèi)存極低的情況下, 系統(tǒng)才會(huì)去考慮回收這種活動(dòng)。
3). 停止?fàn)顟B(tài)
當(dāng)一個(gè)活動(dòng)不再處于棧頂位置, 并且完全不可見的時(shí)候, 就進(jìn)入了停止?fàn)顟B(tài)。 系統(tǒng)仍然會(huì)為這種活動(dòng)保存相應(yīng)的狀態(tài)和成員變量, 但是這并不是完全可靠的, 當(dāng)其他地方需要內(nèi)存時(shí), 處于停止?fàn)顟B(tài)的活動(dòng)有可能會(huì)被系統(tǒng)回收。
4).銷毀狀態(tài)
當(dāng)一個(gè)活動(dòng)從返回棧中移除后就變成了銷毀狀態(tài)。 系統(tǒng)會(huì)最傾向于回收處于這種狀態(tài)的活動(dòng), 從而保證手機(jī)的內(nèi)存充足。
3、Activity的生存期
Activity 類中定義了 7個(gè)回調(diào)方法, 覆蓋了活動(dòng)生命周期的每一個(gè)環(huán)節(jié), 下面就來一一介紹這7個(gè)方法。
口 onCreate() 這個(gè)方法你已經(jīng)看到過很多次了, 每個(gè)活動(dòng)中我們都重寫了這個(gè)方法, 它會(huì)在活動(dòng)第一次被創(chuàng)建的時(shí)候調(diào)用。 你應(yīng)該在這個(gè)方法中完成活動(dòng)的初始化操作, 比如說加載布局、 綁定事件等。
口 onStart() 這個(gè)方法在活動(dòng)由不可見變?yōu)榭梢姷臅r(shí)候調(diào)用。
口 onResume() 這個(gè)方法在活動(dòng)準(zhǔn)備好和用戶進(jìn)行交互的時(shí)候調(diào)用。此時(shí)的活動(dòng)定位于返回棧的棧頂 并且處于運(yùn)行狀態(tài)。
口 onPause() 這個(gè)方法在系統(tǒng)準(zhǔn)備去啟動(dòng)或者恢復(fù)另一個(gè)活動(dòng)的時(shí)候調(diào)用。我們通常會(huì)在這個(gè)方法中將那些消耗 CPU 的資源釋放掉, 以及保存一些關(guān)鍵數(shù)據(jù), 但這個(gè)方法的執(zhí)行速度一定要快, 不然會(huì)影響到新的棧頂活動(dòng)的使用。
口 onStop() 這個(gè)方法在活動(dòng)完全不可見的時(shí)候調(diào)用。 它和 onPause()方法的主耍區(qū)別在于, 如果啟動(dòng)的新活動(dòng)是一個(gè)對話框式的活動(dòng), 那么 onPause()方法會(huì)得到執(zhí)行, 而onStop()方法并不會(huì)執(zhí)行。
口 onDestory() 這個(gè)方法在活動(dòng)被銷毀之前調(diào)用, 之后活動(dòng)的狀態(tài)將變?yōu)殇N毀狀態(tài)。
口 onRestart() 這個(gè)方法在活動(dòng)曰停止?fàn)顟B(tài)變?yōu)檫\(yùn)行狀態(tài)之前調(diào)用, 也就是活動(dòng)被重新啟動(dòng)了。
以上7個(gè)方法中除了onCreate(),其他都是兩兩相對的。在Android的官網(wǎng)上,提供一張非常清晰的示意圖。

為了體驗(yàn)Activity的生命周期,我們可以創(chuàng)建一個(gè)工程來,修改代碼:
public class ExampleActivity extends Activity {
public static final String TAG = "ExampleActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
// The activity is about to be destroyed.
}
}
重新運(yùn)行應(yīng)用,這時(shí)候我們就可以觀察logcat中的打印日志來了解Activity的生命周期。以下是當(dāng) Activity A 啟動(dòng) Activity B 時(shí)一系列操作的發(fā)生順序:
1、Activity A 的 onPause() 方法執(zhí)行。
2、Activity B 的 onCreate()、onStart() 和 onResume() 方法依次執(zhí)行。(Activity B 現(xiàn)在具有用戶焦點(diǎn)。)
3、然后,如果 Activity A 在屏幕上不再可見,則其 onStop() 方法執(zhí)行。
我們可以利用這種可預(yù)測的生命周期回調(diào)順序管理從一個(gè) Activity 到另一個(gè) Activity 的信息轉(zhuǎn)變。 例如,如果必須在第一個(gè) Activity 停止時(shí)向數(shù)據(jù)庫寫入數(shù)據(jù),以便下一個(gè) Activity 能夠讀取該數(shù)據(jù),則應(yīng)在 onPause() 而不是 onStop() 執(zhí)行期間向數(shù)據(jù)庫寫入數(shù)據(jù)。
二、Activity的啟動(dòng)模式
Activity的啟動(dòng)模式一共有四種,我們應(yīng)該根據(jù)特定的需求為每個(gè)Activity指定恰當(dāng)?shù)膯?dòng)模式。這4種模式分別為standard、singleTop、singleTask、singleInstance,可以在AndroidManifest中通過給<Activity>標(biāo)簽指定android:launchMode屬性來選擇啟動(dòng)模式。
1、standard

2、singleTop

3、singleTask

4、singleInstance

前面3個(gè)非常容易理解,第四個(gè)有一些特殊,需要多花一點(diǎn)功夫來理解。singleInstance模式的Activity會(huì)啟動(dòng)一個(gè)新的返回棧來管理這個(gè)Activity。想象以下場景, 假設(shè)我們的程序中有一個(gè)活動(dòng)是允許其他程序調(diào)用的, 如果我們想實(shí)現(xiàn)其他程序和我們的程序可以共享這個(gè)活動(dòng)的實(shí)例, 應(yīng)該如何實(shí)現(xiàn)呢?使用前面3種啟動(dòng)模式肯定是做不到的, 因?yàn)槊總€(gè)應(yīng)用程序都會(huì)有自己的返回棧, 同一個(gè)活動(dòng)在不同的返回棧中人棧時(shí)必然是創(chuàng)建了新的實(shí)例。而使用 singleInstance模式就可以解決這個(gè)問題,在這種模式下會(huì)有一個(gè)單獨(dú)的返回棧來管理這個(gè)活動(dòng), 不管是哪個(gè)應(yīng)用程序來訪問這個(gè)活動(dòng), 都共用的同一個(gè)返回棧, 也就解決了共享活動(dòng)實(shí)例的問題。
實(shí)際舉個(gè)例子
Activity1和Activity3都是standard模式,Activity2是singleInstance模式。
我們從Activity1中啟動(dòng)Activity2,然后在Activity2中啟動(dòng)Activity3。請先想象一下目前為止返回棧中的樣子,因?yàn)锳ctivity2采用了singleInstance模式,它新啟動(dòng)一個(gè)返回棧來管理自己,如果我們添加了taskID的log,就能發(fā)現(xiàn)Activity2的taskID是不同于Activity1和Activity3的。接著我們實(shí)驗(yàn)一下,在Activity3中按下back鍵后,居然直接從Activity3返回到了Activity1,再次按下back鍵又會(huì)返回Activity2,再按back鍵才會(huì)退出。這里面原理很簡單,由于Activity1和Activity3是放在同一個(gè)返回棧中的,所以當(dāng)在Activity3按下back鍵,Activity3就會(huì)出棧,Activity1回到棧頂,因此也就出現(xiàn)了直接從Activity3返回到了Activity1的情況。在Activity1中按下back鍵,這時(shí)返回棧已經(jīng)空了,于是就顯示另一個(gè)返回棧的棧頂活動(dòng),即Activity2。

三、Activity的實(shí)踐
1、記錄當(dāng)前的Activity
業(yè)務(wù)很復(fù)雜,代碼又不是自己寫的時(shí)候,我們想要修改一點(diǎn)界面的東西會(huì)很難找當(dāng)前是哪一個(gè)Activity。這時(shí)候我們可以使用一些技巧來解決這個(gè)問題。
在之前的項(xiàng)目基礎(chǔ)上進(jìn)行修改,創(chuàng)建一個(gè)BaseActivity。這里的BaseActivity和普通的Activity不一樣,因?yàn)槲覀儾恍枰贛anifest中注冊。所以創(chuàng)建一個(gè)java類,然后繼承AppCompatActivity就可以。
public class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseAvtivity", getClass().getSimpleName());
}
}
我們在onCreate()方法中獲取了當(dāng)前實(shí)例的類名,并通過log打印出來。修改其他的Activity的繼承結(jié)構(gòu),讓他們不再繼承AppCompatActivity,而是繼承自BaseActivity。由于BaseActivity又是繼承自AppCompatActivity,所以項(xiàng)目中其他的Activity的現(xiàn)有功能并不受影響,它們?nèi)匀蝗昀^承了Activity中的所有特性。
重新運(yùn)行應(yīng)用,這是運(yùn)行到哪個(gè)Activity,該Activity的類名就會(huì)被打印出來。

2、隨時(shí)隨地退出應(yīng)用
以前面的項(xiàng)目為例,當(dāng)我們在Activity3的時(shí)候,想要退出應(yīng)用需要連續(xù)按3下back鍵才能退出應(yīng)用,這樣顯得很繁瑣,我們需要一個(gè)隨地隨地能退出應(yīng)用的方案。
這里,我們可以用一個(gè)專門的集合類對所有的Activity進(jìn)行管理就可以了。
新建一個(gè)ActivityCollector作為轟動(dòng)管理器:
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for (Activity activity : activities){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
在Activity管理器中,我們通過一個(gè)List暫存Activities,然后提供一個(gè)addActivity
()方法用于想List中添加Activity;提供一個(gè)removeActivity()方法用于從List中移除Activity;最后提供一個(gè)finishAll()方法用于將List中的所有Activity全部銷毀掉。
接下來我們修改BaseActivity中的代碼:
public class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseAvtivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
在BaseActivity的onCreate()方法中調(diào)用了ActivityCollector的addActivity()方法,表示將當(dāng)前的Activity添加到活動(dòng)管理器中。然后在onDestroy()中調(diào)用ActivityCollector的removeActivity()方法,表示將一個(gè)馬上要銷毀的活動(dòng)從活動(dòng)管理器中移除。
現(xiàn)在,不管想要從哪個(gè)地方退出,只要調(diào)用ActivityCollector的finishAll()方法就可以了。
3、啟動(dòng)Activity的另一種寫法
之前我們啟動(dòng)Activity,都是通過構(gòu)建Intent,然后調(diào)用startActivity()或者startActivityForResult()來啟動(dòng)Activity。如果有數(shù)據(jù)要傳遞,也可以借助Intent完成。但是當(dāng)別人需要啟動(dòng)我們開發(fā)的Activity時(shí),并不清楚啟動(dòng)咱們的Activity需要傳遞哪些數(shù)據(jù),所以要么自己看代碼,要么過來問,而顯然這兩種方式都不太現(xiàn)實(shí)。其實(shí)我們只要換一種寫法就可以輕松解決上面的窘境。
public class NewsContentActivity extends BaseActivity {
public static void actionStart(Context context, String newsTitile, String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news Title", newsTitile);
intent.putExtra("news Content", newsContent);
context.startActivity(intent);
}
我們在NewsContentActivity中添加了一個(gè)actionStart()方法,這個(gè)方法中完成了Intent的構(gòu)建,另外所有NewsContentActivity中需要的數(shù)據(jù)都是通過actionStart()方法的參數(shù)傳遞過來的,然后把他們存儲到Intent中,最后調(diào)用startActivity()方法啟動(dòng)NewsContentActivity。
這樣寫的好處就是一目了然,NewsContentActivity所需要的數(shù)據(jù)在方法actionStart()中全部體現(xiàn)了出來。這樣即使不了解內(nèi)部的邏輯,不需要看代碼,別人也能清晰的知道啟動(dòng)NewsContentActivity需要哪些數(shù)據(jù)。另外還簡化了啟動(dòng)Activity的代碼,現(xiàn)在只需要一行代碼就可以啟動(dòng)NewsContentActivity了。
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NewsContentActivity.actionStart(MainActivity.this, "news Title", "news Conten");
}
});
寫代碼需要養(yǎng)成良好的習(xí)慣。。。Activity本身的初探就先告一段落,不管是理論型還是實(shí)踐型我們都已經(jīng)涉及了。從Activity的基本用法,到啟動(dòng)Activity和傳遞數(shù)據(jù)的方式,再到Activity的生命周期,以及Activity的啟動(dòng)模式。最后我們還學(xué)習(xí)了一些實(shí)戰(zhàn)的小技巧,感覺很滿足。