返回棧
你會發現Android中的活動是可以層疊的。我們每啓動一個新的活動,就會覆蓋在原活動之上,然後點擊Back鍵會銷燬最上面的活動,下面的一個活動就會重新顯示出來。
其實Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧裏的活動的集合,這個棧也被稱爲返回棧(Back Stack)。在默認情況下,每當啓動一個新的活動,它會入棧,處於棧頂的位置。而每當我們按下Back或是調用finish方法去銷燬一個活動時,處於棧頂的活動會出棧。
活動狀態
每個活動在生命週期最多有4種狀態。
- 運行狀態:當一個活動處於棧頂位置時,活動處於運行狀態。系統最不願意回收,因爲這會帶來非常差的用戶體驗
- 暫停狀態:當一個活動不再處於棧頂位置,但仍然可見時,活動處於暫停狀態。例如:對話框形式的活動只會佔用中間的部分,處於暫停狀態的活動仍然是完全存活着,系統也不願意去回收這種活動,只用內存極低的情況下,系統纔會去考慮回收這種活動。
- 停止狀態:當一個活動不再處於棧頂位置,且不可見時,活動處於停止狀態。系統仍然會爲這種狀態保存響應的狀態和成員變量,但是這並不是完全可靠的,當其他地方需要內存時,處於停止狀態的活動有可能會被系統回收。
- 銷燬狀態:當一個活動從返回棧中移除後就變成了銷燬狀態。系統會最傾向於回收這種狀態的活動,從而保證手機的內存充足。
活動的生存期
Activity類中定義了7個回調方法,覆蓋了活動生命週期的每一個環節
- onCreate():它會在活動第一次創建的時候調用,你應該在這個方法中完成活動的初始化操作,比如說加載佈局、綁定事件等。
- onStart():這個方法在活動由不可見變爲可見的時候調用。
- onResume():這個方法在系統準備好和用戶進行交互的時候調用。此時的活動一定位於棧頂,並且處於運行狀態
- onPause():這個方法在系統準備去啓動或恢復另一個活動的時候調用。我們通常會在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。
- onStop():這個方法在活動完全不可見的時候調用。它和onPause的主要區別在於,如果啓動的新活動是一個對話框式的活動,那麼onPause會得到執行,而onStop並不會執行
- onDestroy():這個方法在活動被銷燬之前調用
- onReStart():這個方法在活動由停止狀態變爲運行狀態之前調用
各個生存期我們應該做的事情
- 完整生存期:onCreate()→onDestroy(),在前者中完成各種初始化操作,在後者完成釋放內存的操作
- 可見生存期:onStart()→onStop(),通過這兩個方法合理的管理那些對用戶可見的資源,比如在onStart()方法中對資源進行加載,而在onStop()方法中對資源進行釋放
- 前臺生存期:onResume()→onPause(),在前臺生存期內,活動總是處於運行狀態,此時的活動是可以和用戶進行交互的
體驗一下活動的生命週期
我們新建一個 ActivityLifeCycleTest 項目,我們選擇Empty Project,這樣as爲我們創建了一個主活動。然後,我們再創建兩個子活動——NormalActivity和DialogActivity。
接着修改對應的佈局文件就只是顯示一個文本內容"This is a Normal/Dialog Activity"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity"></TextView>
</LinearLayout>
因爲DialogActivity是對話框形式的活動,我們需要再AndroidManifest中添加一個android:theme屬性,Android系統內置有很多主題可以選擇,這裏我們選擇屬性值爲@android:style/Theme.Dialog(對話框式的主題)。我們修改一下主活動的佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/start_normal_activity"
android:text="Start NormalActivity" />
<Button
android:id="@+id/start_dialog_activity"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="Start DialogActivity" />
</LinearLayout>
然後回到主活動中重寫這七個方法:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate");
Button startNormalActivity = (Button)
findViewById(R.id.start_normal_activity);
Button startDialogActivity = (Button)
findViewById(R.id.start_dialog_activity);
// 綁定事件
startNormalActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
NormalActivity.class);
startActivity(intent);
}
});
startDialogActivity.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
DialogActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
}
運行打印輸出:
點擊Normal按鈕:
點擊Back鍵:
點擊Dialog按鈕:
Unable to start activity ComponentInfo: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
這裏報錯:我們回到dialog對應的活動中將public class NormalActivity extends AppCompatActivity
改成public class DialogActivity extends Activity
點擊Back鍵:
再次點擊Back退出程序:
The last 活動被回收了怎麼辦
A啓動了B,A處於停止狀態,這時系統內存不足將A回收了,再次Back會出現什麼情況呢?其實還是會正常顯示活動A的,只不過這時並不會執行onRestart()方法,而是會執行活動A的onCreate()方法。這樣開始在A中用戶進行交互的數據就沒了,例如輸入的個人信息等。爲了提高用戶體驗,Activity中還提供了onSaveInStanceState()回調方法,這個方法可以保證在活動被回收之前一定會被調用,因此我們可以通過這個方法來解決活動被回收時臨時數據得不到保存的問題。
onSaveInstance()方法會攜帶一個Bundle類型的參數,Bundle提供了一系列的方法用於保存數據(鍵值對形式),比如可以使用putString()方法保存字符串…
@Override
protected void onSaveInstance(Bundle outState) {
super.onSaveInstance(outState);
String tempData = "hhh";
outState.putString("data_key", tempData);
}
又沒有發現onCreate函數中有一個Bundle參數,沒錯對應的就是我們存入的outState,只不過一般情況爲null,我們只需要判斷是否爲空即可:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null) {
String tempData = savedInstanceSate.getString("data_key");
Log.d(TAG, tempData);
}
}