Activity 是一個應用組件,用戶可與其提供的屏幕進行交互,以執行撥打電話、拍攝照片、發送電子郵件或查看地圖等操作。 每個 Activity 都會獲得一個用于繪制其用戶界面的窗口。窗口通常會充滿屏幕,但也可小于屏幕并浮動在其他窗口之上。
1、生命周期方法
- onCreate:表示activity被創建,這是生命周期的第一個方法。在這個方法中主要是做一些初始化工作,例如通過setContentView去加載資源文件,初始化數據等。
- onRestart:表示activity重啟,一般當activity有不可見變為課件狀態的時候,onRestart會被調用,例如按home鍵后調用onPause、onStop.再回到頁面會調用onRestart;
- onstart:表示activity正在啟動,即將開始時,這個activity已經可見了,但是還沒有出現在前臺,還無法和用戶交互.如果 Activity 轉入前臺,則后接 onResume(),如果 Activity 轉入隱藏狀態,則后接 onStop()。
- onResume:在 Activity 即將開始與用戶進行交互之前調用。 此時,Activity 處于 Activity 堆棧的頂層,并具有用戶輸入焦點。始終后接 onPause()。
- onPause:當系統即將開始繼續另一個 Activity 時調用。 此方法通常用于確認對持久性數據的未保存更改、停止動畫以及其他可能消耗 CPU 的內容,諸如此類。 它應該非常迅速地執行所需操作,因為它返回后,下一個 Activity 才能繼續執行。如果 Activity 返回前臺,則后接 onResume(),如果 Activity 轉入對用戶不可見狀態,則后接 onStop()。
- onStop:Activity 對用戶不再可見時調用。如果 Activity 被銷毀,或另一個 Activity(一個現有 Activity 或新 Activity)繼續執行并將其覆蓋,就可能發生這種情況。如果 Activity 恢復與用戶的交互,則后接 onRestart(),如果 Activity 被銷毀,則后接 onDestroy()。
- onDestroy:在 Activity 被銷毀前調用,一般做回收工作和最終的資源釋放。這是 Activity 將收到的最后調用。 當 Activity 結束(有人調用 Activity 上的 finish()),或系統為節省空間而暫時銷毀該 Activity 實例時,可能會調用它。 您可以通過 isFinishing() 方法區分這兩種情形。
2、保存 Activity 狀態
當 Activity 暫停或停止時,Activity 對象仍保留在內存中 — 有關其成員和當前狀態的所有信息仍處于 Activity 狀態。 因此,用戶在 Activity 內所做的任何更改都會得到保留,這樣一來,當 Activity 返回前臺(當它“繼續”)時,這些更改仍然存在。
系統為了恢復內存而銷毀某項 Activity 時,Activity 對象也會被銷毀,因此系統在繼續 Activity 時根本無法讓其狀態保持完好,而是必須在用戶返回Activity時重建 Activity 對象。但用戶并不知道系統銷毀 Activity 后又對其進行了重建,因此他們很可能認為 Activity 狀態毫無變化。 在這種情況下,您可以實現另一個回調方法對有關 Activity 狀態的信息進行保存,以確保有關 Activity 狀態的重要信息得到保留:onSaveInstanceState()。
系統會先調用 onSaveInstanceState(),然后再使 Activity 變得易于銷毀。系統會向該方法傳遞一個 Bundle,您可以在其中使用 putString() 和 putInt() 等方法以名稱-值對形式保存有關 Activity 狀態的信息。然后,如果系統終止您的應用進程,并且用戶返回您的 Activity,則系統會重建該 Activity,并將 Bundle 同時傳遞給 onCreate() 和 onRestoreInstanceState()。您可以使用上述任一方法從 Bundle 提取您保存的狀態并恢復該 Activity 狀態。如果沒有狀態信息需要恢復,則傳遞給您的 Bundle 是空值(如果是首次創建該 Activity,就會出現這種情況)。
比如配置發生變化,activity會依次調用onPause、onSaveInstanceState、onStop、onDestory、onCreate、onRestoreInstanceState.
注:無法保證系統會在銷毀您的 Activity 前調用 onSaveInstanceState(),因為存在不需要保存狀態的情況(例如用戶使用“返回” 按鈕離開您的 Activity 時,因為用戶的行為是在顯式關閉 Activity)。 如果系統調用 onSaveInstanceState(),它會在調用 onStop() 之前,并且可能會在調用 onPause() 之前進行調用。
但是,您可能會遇到這種情況:重啟應用并恢復大量數據不僅成本高昂,而且給用戶留下糟糕的使用體驗。 在這種情況下,您有兩個其他選擇:
-
在配置變更期間保留對象
允許 Activity 在配置變更時重啟,但是要將有狀態對象傳遞給 Activity 的新實例。 -
自行處理配置變更
阻止系統在某些配置變更期間重啟 Activity,但要在配置確實發生變化時接收回調,這樣,您就能夠根據需要手動更新 Activity。
2.1在配置變更期間保留對象
如果重啟 Activity 需要恢復大量數據、重新建立網絡連接或執行其他密集操作,那么因配置變更而引起的完全重啟可能會給用戶留下應用運行緩慢的體驗。 此外,依靠系統通過 onSaveInstanceState() 回調為您保存的 Bundle,可能無法完全恢復 Activity 狀態,因為它 并非設計用于攜帶大型對象(例如位圖),而且其中的數據必須先序列化,再進行反序列化, 這可能會消耗大量內存并使得配置變更速度緩慢。在這種情況下,如果 Activity 因配置變更而重啟,則可通過保留 Fragment 來減輕重新初始化 Activity 的負擔。此片段可能包含對您要保留的有狀態對象的引用。
當 Android 系統因配置變更而關閉 Activity 時,不會銷毀您已標記為要保留的 Activity 的片段。您可以將此類片段添加到 Activity 以保留有狀態的對象。
要在運行時配置變更期間將有狀態的對象保留在片段(Fragment)中,請執行以下操作:
- 擴展 Fragment 類并聲明對有狀態對象的引用。
- 在創建片段(Fragment)后調用 setRetainInstance(boolean)。
- 將片段(Fragment)添加到 Activity。
- 重啟 Activity 后,使用 FragmentManager 檢索片段。
例如,按如下所示定義片段(Fragment):
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
注意:盡管您可以存儲任何對象,但是切勿傳遞與 Activity 綁定的對象,例如,Drawable、Adapter、View或其他任何與 Context 關聯的對象。否則,它將泄漏原始 Activity 實例的所有視圖和資源。 (泄漏資源意味著應用將繼續持有這些資源,但是無法對其進行垃圾回收,因此可能會丟失大量內存。)
然后,使用 FragmentManager 將片段添加到 Activity。在運行時配置變更期間再次啟動 Activity 時,您可以獲得片段中的數據對象。 例如,按如下所示定義 Activity:
public class MyActivity extends Activity {
private RetainedFragment dataFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
// load the data from the web
dataFragment.setData(loadMyData());
}
// the data is available in dataFragment.getData()
...
}
@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
dataFragment.setData(collectMyLoadedData());
}
}
在此示例中,onCreate() 添加了一個片段或恢復了對它的引用。此外,onCreate() 還將有狀態的對象存儲在片段實例內部。onDestroy() 對所保留的片段實例內的有狀態對象進行更新。
2.2自行處理配置變更
如果應用在特定配置變更期間無需更新資源,并且因性能限制您需要盡量避免重啟,則可聲明 Activity 將自行處理配置變更,這樣可以阻止系統重啟 Activity。
注:自行處理配置變更可能導致備用資源的使用更為困難,因為系統不會為您自動應用這些資源。 只能在您必須避免Activity因配置變更而重啟這一萬般無奈的情況下,才考慮采用自行處理配置變更這種方法,而且對于大多數應用并不建議使用此方法。
要聲明由 Activity 處理配置變更,請在清單文件中編輯相應的 <activity>
元素,以包含 android:configChanges
屬性以及代表要處理的配置的值。android:configChanges
屬性的文檔中列出了該屬性的可能值(最常用的值包括 "orientation" 和 "keyboardHidden",分別用于避免因屏幕方向和可用鍵盤改變而導致重啟)。您可以在該屬性中聲明多個配置值,方法是用管道 | 字符分隔這些配置值。
例如,以下清單文件代碼聲明的 Activity 可同時處理屏幕方向變更和鍵盤可用性變更:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
現在,當其中一個配置發生變化時,MyActivity 不會重啟。相反,MyActivity 會收到對 onConfigurationChanged() 的調用。向此方法傳遞Configuration 對象指定新設備配置。您可以通過讀取 Configuration 中的字段,確定新配置,然后通過更新界面中使用的資源進行適當的更改。調用此方法時,Activity 的 Resources 對象會相應地進行更新,以根據新配置返回資源,這樣,您就能夠在系統不重啟 Activity 的情況下輕松重置 UI 的元素。
注意:從 Android 3.2(API 級別 13)開始,當設備在縱向和橫向之間切換時,“屏幕尺寸”也會發生變化。因此,在開發針對 API 級別 13 或更高版本系統的應用時,若要避免由于設備方向改變而導致運行時重啟(正如 minSdkVersion
和 targetSdkVersion
屬性中所聲明),則除了 "orientation"值以外,您還必須添加 "screenSize" 值。即,您必須聲明 android:configChanges="orientation|screenSize"。但是,如果您的應用是面向 API 級別 12 或更低版本的系統,則 Activity 始終會自行處理此配置變更(即便是在 Android 3.2 或更高版本的設備上運行,此配置變更也不會重啟 Activity)。
例如,以下 onConfigurationChanged() 實現 檢查當前設備方向:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
Configuration 對象代表所有當前配置,而不僅僅是已經變更的配置。大多數時候,您并不在意配置具體發生了哪些變更,而且您可以輕松地重新分配所有資源,為您正在處理的配置提供備用資源。 例如,由于 Resources 對象現已更新,因此您可以通過 setImageResource() 重置任何 ImageView,并且使用適合于新配置的資源(如提供資源中所述)。請注意,Configuration 字段中的值是與 Configuration 類中的特定常量匹配的整型數。有關要對每個字段使用哪些常量的文檔,請參閱 Configuration參考文檔中的相應字段。
請謹記:在聲明由 Activity 處理配置變更時,您有責任重置要為其提供備用資源的所有元素。 如果您聲明由 Activity 處理方向變更,而且有些圖像應該在橫向和縱向之間切換,則必須在 onConfigurationChanged()
期間將每個資源重新分配給每個元素。
如果無需基于這些配置變更更新應用,則可不用實現 onConfigurationChanged()
。在這種情況下,仍將使用在配置變更之前用到的所有資源,只是您無需重啟 Activity。 但是,應用應該始終能夠在保持之前狀態完好的情況下關閉和重啟,因此您不得試圖通過此方法來逃避在正常 Activity 生命周期期間保持您的應用狀態。 這不僅僅是因為還存在其他一些無法禁止重啟應用的配置變更,還因為有些事件必須由您處理,例如用戶離開應用,而在用戶返回應用之前該應用已被銷毀。
如需了解有關您可以在 Activity 中處理哪些配置變更的詳細信息,請參閱 android:configChanges
文檔和 Configuration 類。
3、啟動模式
Android提供了四種啟動模式:
- standard(默認模式)。系統在啟動 Activity 的任務中創建 Activity 的新實例并向其傳送 Intent。Activity 可以多次實例化,而每個實例均可屬于不同的任務,并且一個任務可以擁有多個實例。
- singleTop(棧頂復用模式)。如果當前任務的頂部已存在 Activity 的一個實例,則系統會通過調用該實例的 onNewIntent() 方法向其傳送 Intent,并且不會調用這個Activity的onCreate、onStart不會被系統調用。因為它并沒有發生改變,而不是創建 Activity 的新實例。,如果新的activity示例已經存在但沒有位于棧頂,就會重新創建一個activity。(比如當前棧為ABCD,A在棧底、D在棧頂、如果再次啟動D,如果D的模式為SingleTop,那么棧內的情況仍然為ABCD,如果D為standard,那么D背重新創建,棧內的情況為ABCDD)。
- singleTask(棧內復用模式)。這是一種單例模式,在這種模式下,只要Activity在一個棧中存在,那么多次啟動此Activity都不會重新創建實例,和singleTop一樣,系統也會回調其 onNewIntent() 方法向其傳送 Intent。具體一點,當一個具有singleTask模式的Activity請求啟動后,比如Activity A,系統會首先尋找是否存在A想要的任務棧,如果存在A所需的任務棧,這個時候就會看A是否在棧中有實例存在,如果有實例存在,那么系統就把A調到棧并調用它的 onNewIntent() ,如果實例不存在,就創建A的實例并把A壓入棧中。例如
- 比如目前的任務棧S1中的情況為ABC,這個時候Activity D以singleTask模式請求啟動,其所需要的任務棧為S2,由于S2和D的實例均不存在,所以系統會創建任務棧S2,然后在創建D的實例并將其壓入到棧S2
- 另外一種情況,假設D所需的任務棧為S1,其他情況跟上面一樣由于S1已經存在,所以直接創建D的實例并將其入棧到S2.
- 如果D所需的任務棧為S1,并且當前的任務棧S1的情況為ADBC,根據棧內復用的原則,此時的D不會重建,系統會把D切換到棧頂并調用其onNewIntent方法,同時由于singleTask默認具有clearTop的效果,會導致棧內所有在D上的Activity全部出棧,于是最終的情況為AD。
- singleInstance(單例模式),加強版的singleTask模式,處了具有singeTask模式的所有特性外,此種模式下的Activity正能單獨的位于一個任務棧中。例如ActivityA,當A啟動后,系統會為它創建一個新的任務棧,然后A獨自在這個新的任務棧中,由于棧內復用的特性,后續的請求均不會創建新的activity,除非這個獨特的任務棧被系統銷毀了。
taskAffinity
用于指定Activity的任務棧,默認情況下所有的Activity的任務棧的名字為應用包名。
當taskAffinity和singleTask啟動模式配對使用,它具有該模式的目前任務棧的名字,待啟動activity會運行在名字和TaskAffinity相同的任務棧中。
給Activity指定啟動模式的方法,即可以在XML文件中配置,也可以在代碼中設置,需要說明的是從優先級上來講,代碼設置的方式要高于XML配置,如果兩種方式同時存在,以代碼設置方式為準,同時XML配置不能指定FLAG_ACTIVITY_CLEAR_TOP,而代碼設置不能指定singleInstance
啟動模式。
xml配置方法如下:
<activity
...
android:launchMode=["multiple"|"singleTop"|"singleTask"|"singleInstance"]
android:taskAffinity="string"
...
/>
代碼中通過Intent指定方法如下:
Intent intent =new Intent(context,AnotherActivity.class)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Intent的設置啟動模式的Flags的值可以為
-
FLAG_ACTIVITY_NEW_TASK
在新任務中啟動 Activity。如果已為正在啟動的 Activity 運行任務,則該任務會轉到前臺并恢復其最后狀態,同時 Activity 會在 onNewIntent() 中收到新 Intent。這會產生與 "singleTask" launchMode 值相同的行為 -
FLAG_ACTIVITY_SINGLE_TOP
如果正在啟動的 Activity 是當前 Activity(位于返回棧的頂部),則 現有實例會接收對 onNewIntent() 的調用,而不是創建 Activity 的新實例。正如前文所述,這會產生與 "singleTop" launchMode 值相同的行為。 -
FLAG_ACTIVITY_CLEAR_TOP
如果正在啟動的 Activity 已在當前任務中運行,則會銷毀當前任務頂部的所有 Activity,并通過 onNewIntent() 將此 Intent 傳遞給 Activity 已恢復的實例(現在位于頂部),而不是啟動該 Activity 的新實例。產生這種行為的 launchMode
屬性沒有值。FLAG_ACTIVITY_CLEAR_TOP 通常與 FLAG_ACTIVITY_NEW_TASK 結合使用。一起使用時,通過這些標志,可以找到其他任務中的現有 Activity,并將其放入可從中響應 Intent 的位置。
注:如果指定 Activity 的啟動模式為 "standard",則該 Activity 也會從堆棧中刪除,并在其位置啟動一個新實例,以便處理傳入的 Intent。 這是因為當啟動模式為 "standard" 時,將始終為新 Intent 創建新實例。
-
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有該標識的activity不會出現在歷史Activity的列表中,當某些情況下我們不希望通過歷史列表回到我們的activity是這個標記比較有用,他等同于XML中指定的Activity屬性android:excludeFromRecents="true"
。