Activity的生命周期
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 有三種不一樣的人生:
用戶返回到該 Activity 就調用 onResume() 方法重新 running
用戶回到桌面或是打開其他 Activity,就會調用 onStop() 進入停止狀態(保留所有的狀態和成員信息,對用戶不可見)
系統內存不足,擁有更高限權的應用需要內存,那么該 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 的匹配過程
加載所有的 Intent Filter列表
去掉 Action 匹配失敗的 Intent Filter
去掉 url 匹配失敗的 Intent Filter
去掉 Category 匹配失敗的 Intent Filter
判斷剩下的 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 )