文章目錄
- Activity 的狀態及生命周期
- 實現管理生命周期
- FirstActivity
- SecondActivity
- DialogActivity
- 運行結果
- 舊活動被回收了還能返回嗎?
Activity 的狀態及生命周期
Android 的應用程序運用 棧(Back Stack) 的思想來管理 Activity:
- 每創建一個新活動,就會覆蓋在舊活動之上,相當于壓入棧。
- 每當按下 返回鍵(Back) 或者調用 finish() ,就會銷毀棧頂的 Activity,相當于彈出棧。
Activity 有四種狀態:
- 運行:在棧頂時運行。
- 暫停:不再處于棧頂、但屏幕上可見時暫停。
- 停止:不再處于棧頂且屏幕上不可見時停止。
- 銷毀:彈棧后銷毀。
Activity 類定義了七個回調方法,覆蓋了整個 Activity 生命周期:
- onCreate() :活動首次創建時;
- onStart() :活動由不可見變為可見;
- onResume() :活動位于棧頂且準備好與用戶交互;
- onPause() :在系統準備去啟動或者恢復另一個活動時調用,通常會釋放一些占用 CPU 的資源,保存一些關鍵數據;
- onStop() :活動完全不可見時調用,如果新活動不是對話框式則調用
onStop
,否則調用onPause
; - onDestroy() :將活動變為銷毀狀態;
- onRestart() :將活動由停止變為運行。
實現管理生命周期
創建三個 Activity 文件以實現研究生命周期。
FirstActivity
FirstActivity
中添加兩個按鈕分別實現啟動 普通活動 和 對話框式活動,并添加七個生命周期方法:
public class FirstActivity extends AppCompatActivity {private static final String TAG = "FirstActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);Button button_normal = (Button)findViewById(R.id.button_normal);button_normal.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(FirstActivity.this,SecondActivity.class);startActivity(intent);}});Button button_dialog = (Button)findViewById(R.id.button_dialog);button_dialog.setOnClickListener((View view)->{Intent intent = new Intent(this, DialogActivity.class);startActivity(intent);});}@Overrideprotected void onStart() {super.onStart();Log.d(TAG, "onStart: ");}@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume: ");}@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "onPause: ");}@Overrideprotected void onStop() {super.onStop();Log.d(TAG, "onStop: ");}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy: ");}@Overrideprotected void onRestart() {super.onRestart();Log.d(TAG, "onRestart: ");}
}
first_layout.xml
實現 FirstActivity
的具體布局:
<?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"><Buttonandroid:id="@+id/button_normal"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="normal"></Button><Buttonandroid:id="@+id/button_dialog"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="dialog"></Button></LinearLayout>
SecondActivity
SecondActivity
作為一個普通活動,供 FirstActivity
的 button_normal
跳轉調用,其 button2
提供一個能撥號 10086 的 intent
:
public class SecondActivity extends AppCompatActivity {private static final String TAG = "SecondActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.second_layout);Button button2 = findViewById(R.id.button_2);button2.setOnClickListener((View view)->{Intent intent = new Intent(Intent.ACTION_DIAL);intent.setData(Uri.parse("tel:10086"));startActivity(intent);});}
}
second_layout.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".SecondActivity"><Buttonandroid:id="@+id/button_2"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="button_2"/></LinearLayout>
DialogActivity
創建 DialogActivity
文件,旨在實現一個對話框式的活動:
public class DialogActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.dialog_layout);}
}
想要讓其實現對話框式,需要對其 activity 標簽做出如下修改:
通過 android:theme 指定 DialogActivity 的主題。
PS: 書上是這么寫的 android:theme="@android:style/Theme.Dialog"
,這是 Activity 的 theme
,但我們默認生成的 DialogActivity
繼承的是 AppCompatActivity,所以就要使用與其配合的 AppCompat 的 theme
才行。
dialog_layout.xml
文件設計布局:
<?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"><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="Dialog Activity"/></LinearLayout>
運行結果
點擊運行,首先顯示的是 FirstActivity
的界面:
此時查看 logcat
,生命周期執行到 onResume
階段:
點擊 NORMAL
按鈕,跳轉到 SecondActivity
界面:
因此 onPause()
—— FirstActivity 啟動另外一個活動 SecondActivity 和 onStop()
—— SecondActivity 需要占滿整個屏幕,因此 FirstActivity 停止,都會被執行:
按下 Back
鍵返回 FirstActivity
界面,觀察 logcat
:
由于 FirstActivity 已經進入停止狀態,因此執行 onRestart()
:由停止變為運行,之后重新執行 onStart()
:由不可見變為可見 和 onResume
:準備好與用戶交互。之所以不執行 onCreate()
,是因為 FirstActivity 沒有重新創建。
點擊 DIALOG
按鈕,跳轉到 DialogActivity
界面:
此時 logcat
只有一條日志信息:
沒有調用 onPause()
是因為 DialogActivity 并沒有占滿整個屏幕,FirstActivity 只是進入了暫停狀態,沒有進入停止狀態。
同理按下 Back
鍵也只會執行 onResume()
方法,因為不用從停止狀態重新變為運行。
最后在 FirstActivity 界面點擊 Back
鍵退出程序,會依次執行:
之后便銷毀了 FirstActivity。
舊活動被回收了還能返回嗎?
A 活動啟動 B 活動,假如此時內存空間不足,系統將 A 活動進行回收了,這時點擊 Back 還能返回 A 嗎?
其實 A 還是會正常顯示的,只不過這時并不會執行 onRestart()
方法,而是執行 oncreate()
方法,A 會被重新創建一次。唯一的問題是 A 中暫存的臨時數據都丟失了。
Activity 中提供了一個 onSaveInstanceState
的回調方法,活動在被回收之前一定會調用該方法,從而解決活動被回收時臨時數據得不到保存的問題。
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {super.onSaveInstanceState(outState);
}
將一些重要的臨時數據保存在 Bundle
中,然后在 onCreate
的 Bundle
變量中提取數據。
onCreate
的 Bundle
一般都為 null
,但是如果在活動被系統回收之前有通過 onSaveInstanceState()
方法來保存數據的話,這個參數就會帶有之前所保存的全部數據,我們只需要再通過相應的取值方法將數據取出即可。