Activity 詳細解析

Activity的生命周期

Activity 生命周期圖.png

Android 中的 Activity 全都歸屬于 Task 管理 。Task 是多個 Activity 的集合,這些 activity 按照啟動順序排隊存入一個棧(即“back stack”)。 Android 默認會為每個 app 維持一個 Task 來存放該 app 的所有 Activity,Task 的默認 name 為該 app 的 packagename 。

首先打開一個新的 Activity 實例的時候,系統會依次調用 onCreate() -> onStart() -> onResume() 然后開始 running, 在 running 的時候被覆蓋了(從它打開了新的 Activity 或是被鎖屏,但是它依然在前臺運行, lost focus but is still visible),系統調用onPause(),該方法執行 Activity 暫停,通常用于提交未保存的更改到持久化數據,停止動畫和其他的東西。但這個 Activity 還是完全活著(它保持所有的狀態和成員信息,并保持連接到窗口管理器)

接下來這個 Activity 有三種不一樣的人生:

  1. 用戶返回到該 Activity 就調用 onResume() 方法重新 running

  2. 用戶回到桌面或是打開其他 Activity,就會調用 onStop() 進入停止狀態(保留所有的狀態和成員信息,對用戶不可見

  3. 系統內存不足,擁有更高限權的應用需要內存,那么該 Activity 的進程就可能會被系統回收(回收onRause()和onStop()狀態的 Activity 進程)要想重新打開就必須重新創建一遍。

如果用戶返回到 onStop() 狀態的 Activity(又顯示在前臺了),系統會調用 onRestart() -> onStart() -> onResume() 然后重新 running 在 Activity 結束(調用finish () )或是被系統殺死之前會調用 onDestroy() 方法釋放所有占用的資源。

Activity 生命周期中三個嵌套的循環:

  • Activity 的完整生存期會在 onCreate() 調用和 onDestroy() 調用之間發生。

  • Activity 的可見生存期會在 onStart() 調用和 onStop() 調用之間發生。系統會在 Activity 的整個生存期內多次調用 onStart() 和 onStop(), 因為 Activity 可能會在顯示和隱藏之間不斷地來回切換。

  • Activity 的前后臺切換會在 onResume() 調用和 onPause() 之間發生。 因為這個狀態可能會經常發生轉換,為了避免切換遲緩引起的用戶等待,這兩個方法中的代碼應該相當地輕量化

Activity 的啟動模式

啟動模式什么?

簡單的說就是定義 Activity 實例與 Task 的關聯方式。

為什么要定義啟動模式?

為了實現一些默認啟動(standard)模式之外的需求:

  • 讓某個 Activity 啟動一個新的 Task (而不是被放入當前 Task )

  • 讓 Activity 啟動時只是調出已有的某個實例(而不是在 back stack 頂創建一個新的實例)

  • 或者,你想在用戶離開 Task 時只保留根 Activity,而 back stack 中的其它 Activity 都要清空

怎樣定義啟動模式?

定義啟動模式的方法有兩種:

使用 manifest 文件

在 manifest 文件中 Activity 聲明時,利用 Activity 元素的 launchMode 屬性來設定 Activity 與 Task 的關系。

注意: 你用 launchMode 屬性為 Activity 設置的模式可以被啟動 Activity 的 intent 標志所覆蓋。

####### 有哪些啟動模式?

  • "standard"(默認模式)

    當通過這種模式來啟動 Activity 時, Android 總會為目標 Activity 創建一個新的實例,并將該 Activity 添加到當前Task棧中。這種方式不會啟動新的 Task,只是將新的 Activity 添加到原有的 Task 中。

  • "singleTop"

    該模式和standard模式基本一致,但有一點不同:當將要被啟動的 Activity 已經位于 Task 棧頂時,系統不會重新創建目標 Activity 實例,而是直接復用 Task 棧頂的 Activity。

  • "singleTask"

    Activity 在同一個 Task 內只有一個實例。如果將要啟動的 Activity 不存在,那么系統將會創建該實例,并將其加入 Task 棧頂。

    如果將要啟動的 Activity 已存在,且存在棧頂,直接復用 Task 棧頂的 Activity。

    如果 Activity 存在但是沒有位于棧頂,那么此時系統會把位于該 Activity 上面的所有其他Activity全部移出 Task,從而使得該目標 Activity 位于棧頂。

  • "singleInstance"

    無論從哪個 Task 中啟動目標 Activity,只會創建一個目標 Activity 實例且會用一個全新的 Task 棧來裝載該 Activity 實例(全局單例)

    如果將要啟動的 Activity 不存在,那么系統將會先創建一個全新的 Task ,再創建目標 Activity 實例并將該 Activity 實例放入此全新的 Task 中。

    如果將要啟動的Activity已存在,那么無論它位于哪個應用程序,哪個 Task 中;系統都會把該 Activity 所在的 Task 轉到前臺,從而使該 Activity 顯示出來。

使用 Intent 標志

在要啟動 activity 時,你可以在傳給 startActivity() 的 intent 中包含相應標志,以修改 activity 與 task 的默認關系。

like this

Intent i = new Intent(this,NewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);

####### 可以通過標志修改的默認模式有哪些?

  • FLAG_ACTIVITY_NEW_TASK

    與 "singleTask" 模式相同,在新的 task 中啟動 Activity。如果要啟動的 Activity 已經運行于某 Task 中,則那個 task 將調入前臺。

  • FLAG_ACTIVITY_SINGLE_TOP

    與 "singleTop" 模式相同,如果要啟動的 Activity位于back stack 頂,系統不會重新創建目標 Activity 實例,而是直接復用 Task 棧頂的 Activity。

  • FLAG_ACTIVITY_CLEAR_TOP

    此種模式在 launchMode 中沒有對應的屬性值。

    如果要啟動的 Activity 已經在當前 Task 中運行,則不再啟動一個新的實例,且所有在其上面的 Activity 將被銷毀。

    一般不要改變 Activity 和 Task 默認的工作方式。 如果你確定有必要修改默認方式,請保持謹慎,并確保 Activity 在啟動和從其它 Activity 返回時的可用性,多做測試和安全方面的工作。

Activity被回收之后狀態的保存和恢復

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if(savedInstanceState!=null){ //判斷是否有以前的保存狀態信息
            savedInstanceState.get("Key"); 
        }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //可能被回收內存前保存狀態和信息,
        Bundle data = new Bundle(); 
        data.putString("key", "last words before be kill");
        outState.putAll(data);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      if(savedInstanceState!=null){ //判斷是否有以前的保存狀態信息
        savedInstanceState.get("Key"); 
       }
     super.onRestoreInstanceState(savedInstanceState);
    }
}
onSaveInstanceState 方法

在 Activity 可能被回收之前調用,用來保存自己的狀態和信息,以便回收后重建時恢復數據(在 onCreate() 或 onRestoreInstanceState() 中恢復)。旋轉屏幕重建 Activity 會調用該方法,但其他情況在 onRause() 和 onStop() 狀態的 Activity 不一定會調用,下面是該方法的文檔說明。

One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

也就是說,系統靈活的來決定調不調用該方法,但是如果要調用就一定發生在 onStop 方法之前,但并不保證發生在 onPause 的前面還是后面。

onRestoreInstanceState 方法

這個方法在 onStart 和 onPostCreate 之間調用,在 onCreate 中也可以狀態恢復,但有時候需要所有布局初始化完成后再恢復狀態。

onPostCreate:一般不實現這個方法,當程序的代碼開始運行時,它調用系統做最后的初始化工作。

Intent Filter

Android 的 3 個核心組件—— Activity、Services、廣播接收器——是通過 intent 傳遞消息的。 intent 消息用于在運行時綁定不同的組件。   在 Android 的 AndroidManifest.xml 配置文件中可以通過 intent-filter 節點為一個 Activity 指定其 Intent Filter,以便告訴系統該 Activity 可以響應什么類型的 Intent。

intent-filter 的三大屬性
  • Action

    一個 Intent Filter 可以包含多個 Action,Action 列表用于標示 Activity 所能接受的“動作”,它是一個用戶自定義的字符串。

    <intent-filter > 
        <action android:name="android.intent.action.MAIN" />    
        <action android:name="com.scu.amazing7Action" /> 
        ……
    </intent-filter>

在代碼中使用以下語句便可以啟動該Intent 對象:

> Intent i=new Intent(); 
> i.setAction("com.scu.amazing7Action");

Action 列表中包含了“com.scu.amazing7Action”的 Activity 都將會匹配成功
  • URL

    在 intent-filter 節點中,通過 data 節點匹配外部數據,也就是通過 URI 攜帶外部數據給目標組件。

        <data android:mimeType="mimeType" 
              android:scheme="scheme" 
              android:host="host"
              android:port="port" 
          android:path="path"/>
> 注意:只有data的所有的屬性都匹配成功時 URI 數據匹配才會成功
  • Category

    指定 Action 范圍,這個選項指定了將要執行的這個 Action 的其他一些額外的約束.有時通過 Action,配合 Data 或 Type ,很多時候可以準確的表達出一個完整的意圖了,但也會需要加一些約束在里面才能夠更精準。

    <intent-filter . . . >
        <action android:name="code android.intent.action.MAIN" />
        <category android:name="code android.intent.category.LAUNCHER" />
    </intent-filter>
Activity 中 Intent Filter 的匹配過程
  1. 加載所有的 Intent Filter列表

  2. 去掉 Action 匹配失敗的 Intent Filter

  3. 去掉 url 匹配失敗的 Intent Filter

  4. 去掉 Category 匹配失敗的 Intent Filter

  5. 判斷剩下的 Intent Filter 數目是否為 0 。如果為 0 查找失敗返回異常;如果大于 0 ,就按優先級排序,返回最高優先級的 Intent Filter

一般設置Activity為非公開的

<activity 
...... 
android:exported="false" />

注意:非公開的 Activity 不能設置 Intent-filter,以免被其他 Activity 喚醒(如果擁有相同的 Intent-filter )。

  • 不要指定 Activity 的 taskAffinity 屬性

  • 不要設置 Activity 的 LaunchMode(保持默認)

    注意 Activity 的 intent 最好也不要設定為 FLAG_ACTIVITY_NEW_TASK

  • 在匿名內部類中使用 this 時加上 Activity 類名(類名.this,不一定是當前 Activity )

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容