Android開發(fā)學(xué)習(xí) -- Day4 探究Activity(三)

一、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)上,提供一張非常清晰的示意圖。

Activity生命周期

為了體驗(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)的小技巧,感覺很滿足。

關(guān)注獲取更多內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Day1: 在代碼中通過R.string.hello_world可以獲得該字符串的引用; 在XML中通過@stri...
    冰凝雪國閱讀 1,614評論 0 5
  • 【快來開啟AR捉貓的虛擬奇幻之旅~】,復(fù)制這條信息¥5q3903iYZ34¥后打開手機(jī)淘寶
    zolipi閱讀 278評論 0 0
  • 面容矍鑠思路廣,滿面笑容訴腰傷。 雖是本生粵籍人,國語對話卻流暢。 皮包骨頭雙膝殘,神情干練不緊張。 壽星術(shù)中巧配...
    徐一村閱讀 308評論 1 5

友情鏈接更多精彩內(nèi)容