【Android】四大組件_Activity小結

該系列主要是記錄、回顧之前的學習和一些筆記。
轉載請註明!


Activity在應用中的表現爲界面,它會加載指定的佈局文件來顯示各種UI元素,同時,用戶可以和這些UI元素進行交互。App便是由一個或多個Activity組成。


Activity生命週期

  • Activity的生命週期示意圖

    image

  • Activity生命週期包含最主要的7個生命週期函數,分別是onCreate(),onStart(),onResume(),onPause(),onStop(),onDestroy(),onRestart()。

    • onCreate():在Activity第一次被創建的時候調用,並且在該方法中進行Activity的初始化操作,如加載佈局、綁定事件。
    • onStart():該方法在Activity由不可見變爲可見的時候調用。在Activity的onCreate()函數調用之後被調用,此時的Activity還處在不可見狀態,當Activity馬上可見時,onStart() 就會被調用。
    • onResume():該方法在Activity變爲可見狀態,準備好和用戶進行交互的時候調用,此時的Activity一定位於返回棧的棧頂,並且處於運行狀態。並且在執行完該方法後,Activity就會請求AMS(AMS介紹)渲染它所管理的視圖。
    • onPause():該方法在系統準備啓動或恢復另一個Activity的時候調用。也就是當Activity的狀態即將由可見狀態變爲不可見狀態時調用。
    • onStop():該方法在Activity完全不可見的時候調用。該方法與onPause()方法不同點在於,當新啓動是一個對話框的Activity,那麼onPause()會執行,而onStop()不會。
    • onDestroy():該方法會在Activity被銷燬之前調用,之後的Activity狀態將變爲銷燬狀態。一般Activity的內存釋放也會在該時期進行。
    • onRestar():該方法會在Activity由Stop狀態變爲Start狀態之前調用。當Activity由完全不見重新可以和用戶交互的時候會調用。
  • 在Activity生命週期中,還有額外的兩個回調方法:onSaveInstanceState(),onRestoreInstanceState()。這兩個方法會在特定的時期被觸發。

    • onSaveInstanceState():當Activity有可能被銷燬,但是還沒有被銷燬時,該Activity的onSaveInstanceState函數就會被執行,並且該方法會保存當前Activity的狀態。除非該Activity是被用戶主動銷燬的,如當用戶按back鍵時。例如:
      • 用戶從當前Activity切換到桌面;
      • 長按Home鍵,選擇運行其他的程序時;
      • 按下電源鍵(關閉屏幕顯示)時;
      • 從Activity A啓動一個新的Activity時;
      • 屏幕方向切換時,如從橫屏切換到豎屏;
      • 電話打入等情況發生時;
      • 總結一句話,一切讓當前的Activity徹底在屏幕上看不見,但是沒有主動銷燬Activity的操作都會觸發該方法。
    • onRestoreInstanceState():當Activity確實被系統重新回收,重新創建時纔會調用。該方法可以取出onSaveInstanceState()方法保存的數據,並用這些數據恢復之前Activity的狀態。值得注意的一點是,在onSaveInstanceState()方法中保存的數據,我們可以在onCreate()和onSaveInstanceState()方法中獲取到,這兩個方法的區別在於,onSaveInstanceState()方法一旦被調用,就表明onSaveInstanceState()方法的Bundle參數一定是有值的,不需要做額外的null判斷。而onCreate()方法需要。
  • 我們可以將Activity的完整的生命週期分爲三個部分。

    • 完整生存期(Activity在onCreate()方法和onDestroy()方法之間所經歷過程)。這兩個方法分別標示Activity的創建和銷燬,並且在Activity整個生命週期中只會調用一次。
    • 可見生存期(Activity在onStart()方法和onStop()方法之間所經歷過程)。在該時期內,Activity總是可見的。這兩個方法會隨着用戶的操作可調用多次。
    • 前臺生存期(Activity在onResume(),onPause()方法之間所經歷過程)。在該時期內,Activity總是處於運行狀態,此時的活動是可以和用戶進行交互的。這兩個方法會隨着用戶的操作可調用多次。
  • 特定操作的方法回調:

    1. 第一次啓動(A)Activity,回調:(A)onCreate()->(A)onStart()->(A)onResume();
    2. 用戶從(A)Activity打開新的(B)Activity,回調如下:(A)onPause()->(B)onCreate()->(B)onStart()->(B)onResume()->(A)onSaveInstanceState()->(A)onStop();
    3. 從(B)Activity返回(A)Activity,回調如下:(B)onPause()->(A)onRestart()->(A)onStart()->(A)onResume->(B)onStop()->(B)onDestroy();
    4. 用戶從(A)Activity打開一個透明主題或是Dialog形式的(B)Activity,回調如下:(A)onPause()->(B)onCreate()->(B)onStart()->(B)onResume();
    5. 從4.的(B)Activity返回(A)Activity,回調如下:(B)onPause()->(A)onResume()->(B)onStop()->(B)onDestroy();
    6. 按home鍵,(A)Activity返回到桌面時,回調如下:(A)onPause()->(A)onSaveInstanceState()->(A)onStop();
    7. 從桌面返回(A)Activity時,回調如下:(A)onRestart()->(A)onStart()->(A)onResume;
    8. 按back鍵回退(A)Activity時,回調如下:(A)onPause()->(A)onStop()->(A)onDestroy();
  • 異常情況下操作的方法回調

    • 旋轉屏幕,使其重建Activity,回調如下:(A)onPause()->(A)onSaveInstanceState()->(A)onStop()->(A)onDestroy()->(重建的A)onCreate()->(重建的A)onStart()->(重建的A)onRestoreInstanceState()->(重建的A)onResume();
  • 當我們不想讓Activity在異常情況下重建,該怎麼辦?答案是給Activity指定configChanges屬性。在指定屬性後,當Activity異常銷燬時便不再調用onSaveInstanceState()、onRestoreInstanceState()方法,取而代之會執行onConfigurationChanged()方法。


Activity的啓動模式

應用程序都是由一個或多個Activity組成的,Activity的實例由Android內部的任務棧來管理。棧是一種先進後出的集合。對於Android來說,當前顯示的Activity必定位於棧頂,當按回退back鍵時,棧頂的Activity便會被移除任務棧,位於被移除任務棧的Activity下面的Activity成爲新的棧頂,同時顯示。

至於爲什麼需要劃分啓動模式,是爲了更好地管理Activity,例如:當我們只需要一個Activity只有一個實例,那麼啓動模式便可以幫助我們實現。

  • 四種啓動模式

    • standard(標準啓動模式),該模式也是Activity的默認啓動模式。在該模式下,每次啓動一個Activity都會創建一個新的實例,不管該Activity是否在任務棧中存在。同時,無論誰啓動該Activity,該Activity都會進入啓動它的Activity所屬的任務棧。例如:棧A的Activity啓動了一個新的Activity,新的Activity會進入棧A。
    • singleTop(棧頂複用模式),該模式下啓動的Activity,會先判斷棧頂該Activity的實例是否存在,若存在,則會重用位於棧頂的實例,並且會調用該實例的onNewIntent()方法將新的Intent對象傳遞到這個實例中。需要注意的是,在複用實例後,這個Activity的onCreate、onStart不會被重新調用,而是調用的onNewIntent、onResume方法。如果新的Activity的實例不在棧頂,那麼任然會創建新的Activity實例。
    • singleTask(棧內複用模式),該模式下啓動Activity,那麼棧內只能存在一個該Activity的實例。創建該模式的Activity,首先會在棧內尋找是否有實例,有的話會將該實例頂上的其他Activity移除任務棧(銷燬),讓該Activity位於棧頂;沒有的話則會創建一個實例並放在棧頂。
    • singleInstance(單實例模式),加強版singleTask,該模式下的Activity只能單獨位於一個任務棧中,這個任務棧也只會有這一個Activity。singleTask模式下的Activity可以擁有多個位於不同棧的實例,而singleInstance模式下的Activity就只有一個。
  • TaskAffinity和allowTaskReparenting

    • TaskAffinity是標示Activity所需任務棧名字的參數。

      • 在默認情況下,Activity所需任務棧的名字爲應用的包名。我們也能爲每個Activity設置TaskAffinity屬性來自定義任務棧名,但是,TaskAffinity屬性值不能與包名重複(要是重複還設置了幹啥)。
      • TaskAffinity一般和singleTask啓動模式和allowTaskReparenting屬性搭配使用,其他情況下該屬性設置了也無用。
      • 在一個包名爲“A”的項目中,啓動一個TaskAffinity屬性爲“B(B的結構需要和包名類似,如“xxx.xxx.xxx”)”,啓動模式爲singleTask的Activity。該Activity首先會判斷名稱爲“B”的任務棧是否存在,存在的話在判斷是否有該Activity的實例,實例存在,則將該實例置於棧頂;不存在則創建一個至於棧頂。若任務棧不存在,則創建任務棧,再創建實例。
    • allowTaskReparenting屬性常用於和TaskAffinity屬性搭配使用,主要功能界定是棧A中的Activity是否能遷移到棧B中去,而遷移跟Activity的TaskAffinity有關。舉個栗子:應用A有甲Activity,應用B有乙Activity,應用A啓動應用B的乙。這時,應用A的棧中有甲乙兩個Activity,按home回到桌面,啓動應用B,這時會發現,應用A的棧中的乙去到了應用B的棧。當然,這是allowTaskReparenting屬性爲true的情況,如果爲false,則應用A的棧不變,應用B的棧內會重新生成一個新的乙。
      大佬博客更詳細描寫

  • 啓動模式的設置

    • 通過AndroidMenifest爲Activity指定啓動模式:

      <activity android:name=".Activity_A"
          android:launchMode="singleTask"
          android:allowTaskReparenting="flase"
          android:taskAffinity="com.angki.a"/>
          
      android:launchMode: 設置啓動模式
      android:taskAffinity:設置taskAffinity
      android:allowTaskReparenting:設置allowTaskReparenting
      
    • 通過在Intent設置標誌位來指定啓動模式:

      Intent intent = new Intent(MainActivity.this, Activity_A.class);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      startActivity(intent);    
      
    • 兩種啓動方式的區別,第二種優先級高於第一種,當兩種方式同時存在時,以第二種方式爲準。

  • 常用標記位

    • FLAG_ACTIVITY_NEW_TASK,新建一個Task來啓動Activity,和清單文件指定SingleTask啓動模式效果相同。
    • FLAG_ACTIVITY_SINGLE_TOP,和清單文件指定SingleTop啓動模式效果相同。
    • FLAG_ACTIVITY_CLEAR_TOP,清除該啓動Activity同棧中所有位於它上面的Activity,和清單文件指定SingleTask啓動模式效果相同。
    • FLAG_ACTIVITY_NO_HISTORY,以該模式啓動的Activity在啓動其他Activity會自己銷燬自己。
    • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,使用該模式啓動的Activity不添加到最近應用列表,與屬性android:excludeFromRecents="true"效果相同。需要注意的是,要想應用列表不顯示該Activity,需要一個不同的棧來存放。因爲該應用列表不顯示是把該Activity所在任務棧置於後臺任務棧。舉個例子:
      • AB兩個Activity同在一個棧內,其中B爲FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS模式啓動的。當前頁面顯示B頁面,點擊多任務管理按鍵,發現顯示的仍是B頁面。

      • Main、A兩個Activity不在一個棧內,其中A爲FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS模式啓動的。查看棧顯示:

        Running activities (most recent first):
            TaskRecord{2becf13d #7042 A=com.angki.task U=0 sz=1}
                Run #1: ActivityRecord{18df41f7 u0 com.angki.androidlearn/.Activity_A t7042}
            TaskRecord{3c3ba83 #7041 A=com.angki.androidlearn U=0 sz=1}
                Run #0: ActivityRecord{2d080907 u0 com.angki.androidlearn/.MainActivity t7041}
        
        

        點擊點擊多任務管理按鍵:

        Running activities (most recent first):
            TaskRecord{3c3ba83 #7041 A=com.angki.androidlearn U=0 sz=1}
                Run #1: ActivityRecord{2d080907 u0 com.angki.androidlearn/.MainActivity t7041}
            TaskRecord{2becf13d #7042 A=com.angki.task U=0 sz=1}
                Run #0: ActivityRecord{18df41f7 u0 com.angki.androidlearn/.Activity_A t7042}
        
        

        可以發現,A的棧置於後,當前顯示的是Main頁面。

      • ABC三個Activity,A屬於棧a,BC屬於棧b,其中B爲FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS模式啓動的。當前顯示C頁面,點擊點擊多任務管理按鍵,當前顯示A頁面,說明棧b被後置。按下back鍵,頁面顯示C頁面,再按下back鍵,頁面顯示B頁面,再按下back鍵,退出程序。

參考來源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章