【Android】安卓四大元件之Activity(二)

woodwhale發表於2022-01-18

【Android】安卓四大元件之Activity(二)

前言

在這篇文章之前,我已經寫過了一篇有關Activity的內容,是關於activity之間的頁面跳轉和資料傳遞,而這篇文章著重強調的是Activity中的有關生命週期的理解。

1、什麼是生命週期?

在之前學習Java的時候,Java中的一個類的物件就涉及到了生命週期,包括它的生成、作用、回收等等。

在Android中也有差不多的生命週期的概念,是針對Activity的。

首先,給出安卓開發文件中對生命週期的介紹:瞭解 Activity 生命週期

在官方文件的基礎上,我們來理解各個生命週期!

當使用者瀏覽、退出和返回到您的應用時,應用中的 Activity 例項會在其生命週期的不同狀態間轉換,而為了在 Activity 生命週期的各個階段之間導航轉換,Activity 類提供六個核心回撥:onCreate()onStart()onResume()onPause()onStop()onDestroy()。當 Activity 進入新狀態時,系統會呼叫其中每個回撥。

以下是官方文件對六大生命週期回撥方法的簡化檢視:

img

2、onCreate()

2.1 基本解析

因為生命週期是從上向下執行的,我們首先分析最開始的onCreate()方法

onCreate(),這個是activity被首次建立的時候呼叫的方法,而且我們必須在每個activity中重寫該方法!這個方法在activity生命週期中只出現一次,如果第二次出現,那麼說明上一個activity已經呼叫了onDestroy()方法,被銷燬了。

相信你對下面的程式碼不陌生,因為每個activity中都會有這樣類似作用的程式碼!

private TextView photo;
private ImageView pic;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_camera);

    photo = this.findViewById(R.id.tv_photograph);
    pic = this.findViewById(R.id.iv_pic);
    photo.setOnClickListener(this);
}

在開始構建activity的時候,我們在onCreate()方法中我們都會會將資料繫結到列表,將 Activity與各個元件相關聯,並例項化某些類作用域變數。

我們在上述的程式碼例子中可以發現,我們例項化了photo這個TextView還有pic這個ImageView,並且在此之前,我們一定會給當前這個activity繫結一個xml佈局檔案,通過呼叫setContentView()方法,然後通過R.layout找到我們需要的佈局xml進行繫結。

2.2 你可能疑惑的savedInstanceState

以下內容感興趣的可以看看,初學者如果不明白其實也不需要太懂。

細心的你會發現,onCreate()其實傳入了一個Bundle類的物件引數savedInstanceState,這是個啥玩意?

接下來,我們對savedInstanceState進行詳細解釋!

首先我們分析以下Bundle類是啥,說通俗一點,就是實現了Parcelable介面的一個鍵值對

官方的簡介是:

A mapping from String keys to various Parcelable values.

也就是說,這個類例項化的物件是可以儲存資料的,並且是以鍵值對的形式儲存的,實現了Parcelable介面

而傳入的savedInstanceState物件,字面翻譯就是儲存過的例項狀態,並且我們上面說了,savedInstanceState都物件可以儲存鍵值對,也就是說有兩種情況:

  • savedInstanceState不為null
  • savedInstanceState是null的

事實上,savedInstanceState為null的情況是最常見的,但是什麼情況savedInstanceState不為空呢?

2.2.1 onSaveInstanceState()方法

根據文件,我們發現,Activity類中有一個onSaveInstanceState()方法,不同於onCreate()這種生命週期方法,onSaveInstanceState()只有在進入某種“activity有被殺死的風險”的狀態下,才會被呼叫

會執行onSaveInstanceState()方法的情況,官方文件中是如下解釋的:

 Android calls onSaveInstanceState() before the activitybecomes vulnerable to being destroyed by the system, but does not bothercalling it when the instance is actually being destroyed by a user action (suchas pressing the BACK key).

當某個activity變得"容易"被系統銷燬時,該activity的onSaveInstanceState()就會被執行,除非該activity是被使用者主動銷燬的,例如當使用者按BACK鍵的時候。

通俗一點,就是進入某種“activity有被殺死的風險”狀態,onSaveInstanceState()方法就會被呼叫

也許你想到了很多的情況,我總結了一共如下的情況會呼叫onSaveInstanceState()方法:

  • 當使用者按下HOME鍵後。(這種情況,系統不知道你按下HOME後要執行多少其他的程式,自然也不知道activity A是否會被銷燬,因此係統會呼叫onSaveInstanceState(),讓使用者有機會儲存某些非永久性的資料)
  • 調出程式管理,選擇執行其他的程式時。(同樣是可能記憶體不夠被系統kill掉)
  • 按下息屏鍵關閉螢幕時(一樣的道理,手機廠家會設定息屏後程式的狀態,也可能被kill)
  • 從activity A中啟動一個新的activity B時。(例如從QQ開啟某tx遊戲,遊戲的資源呼叫很大,可能把QQ的程式kill掉,雖然大多數情況我們是給QQ後臺許可權的)
  • 螢幕方向切換時,例如從豎屏切換到橫屏時。(在螢幕切換時,系統會銷燬activity A,在螢幕切換之後系統又會自動地建立activity A,所以onSaveInstanceState()一定會被執行,且也一定會執行onRestoreInstanceState()

說專業一點:只要某個Activity是做入棧並且非棧頂時(啟動跳轉其他Activity或者點選Home按鈕),此Activity是需要呼叫onSaveInstanceState()的, 如果Activity是做出棧的動作(點選back或者執行finish),是不會呼叫onSaveInstanceState的。

2.2.2 onRestoreInstanceState()方法

onSaveInstanceState()對應的是onRestoreInstanceState()方法,但是——這兩個方法並不是成對出現,執行了``onSaveInstanceState()不一定執行onRestoreInstanceState()`

onRestoreInstanceState()執行的條件是:只有在Activity真的被系統非正常殺死過,恢復顯示Activity的時候,就會呼叫onRestoreInstanceState()

簡單來說,執行了onSaveInstanceState()儲存了資料狀態,並不一定會呼叫onRestoreInstanceState()來返回狀態,但是如果確實時非正常的kill程式,那麼會呼叫onRestoreInstanceState()返回onSaveInstanceState()儲存的資料

我們可以看看安卓開發文件中給出的例子:

TextView textView;

// some transient state for the activity instance
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState);

    // recovering the instance state
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // initialize member TextView so we can manipulate it later
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// invoked when the activity may be temporarily destroyed, save the instance state here
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // call superclass to save any view hierarchy
    super.onSaveInstanceState(outState);
}

可以看到重寫的onSaveInstanceState()方法中存入了遊戲狀態textView的文字,在onRestoreInstanceState()方法中,呼叫了獲取當前文字並設定的方法,並且我們可以在這個例子中看到onCreate()方法中是如何利用savedInstanceState這個物件的——不為空那麼就獲取資料

if (savedInstanceState != null) {
    gameState = savedInstanceState.getString(GAME_STATE_KEY);
}

3、onStart()

當 Activity 進入“onStart”狀態時,系統會呼叫onStart()方法。onStart() 呼叫使 Activity 對使用者可見,也就是在這個方法中,我們可以看到app的前端activity展示了

onStart() 方法會非常快速地完成,並且與onCreate()一樣,Activity 不會一直處於“onStart”狀態。一旦此回撥結束,Activity 便會進入“onResume”狀態,系統將呼叫 onResume() 方法。

4、onResume()

onResume是應用與使用者互動的狀態,也就是具有焦點,我們可以對app中的各種元件進行操作的一個焦點狀態,這個時候是使用者與應用的互動的狀態。

5、onPause()

onPause狀態是使用者對這個activity失去焦點,但是onPause這個狀態,使用者還是對activity可見的。

舉個例子,有兩個activity,第一個activity A,第二個activity B。

如果A使用透明主題,B使用預設主題。當由A通過Intent跳轉到B時,會失去A的焦點,呼叫onPause()方法,但是,因為時透明主題,所以我們在看B的同時,可以看到A,也就是A仍然是可見的,所以A不會呼叫onStop(),也就是不會被停止

如果這個時候我們點選back按鈕,從B返回到了A,A會重新呼叫onResume()方法,因為A得到了焦點,但是並不會呼叫onStart(),因為我們的A從來沒有被停止過,仍然有可見的介面

6、onStop()

onStop狀態就是我們對這個activity失去了焦點,但是它並未被銷燬

舉一個常見的例子——微信掃一掃,我們從微信主頁開啟微信掃一掃,主頁失去了焦點並且不可見,成為了onStop的狀態,返回之後主頁又得到了焦點,執行了onStart()onResume()

7、onDestroy()

onDestroy被執行,呼叫此回撥的原因如下:

  1. Activity 即將結束(由於使用者徹底關閉Activity或由於系統為Activity呼叫finish()方法
  2. 由於配置變更(例如裝置旋轉多視窗模式),系統暫時銷燬Activit

如果Activity即將結束,onDestroy()是 Activity 收到的最後一個生命週期回撥。如果由於配置變更而呼叫 onDestroy(),系統會立即新建 Activity 例項,然後在新配置中為新例項呼叫onCreate()

8、橫豎屏的影響

在理解了各個部分的生命週期之後,我們應該注意到,在APP中橫豎屏的切換,會導致生命週期改變——先銷燬、再建立一個新的。

那麼我們可能遇到這樣的情況——我們豎屏看電影的時候,進度條在20分鐘,但是如果我們切換成了橫屏進度條就從頭開始了。這種APP出現的問題就是沒有處理橫豎屏切換帶來的影響。

之所以會出現上述情況,是因為activity的一個生命週期已經結束了,橫屏進入了一個新的生命週期。

解決方法有兩種:

  • 在activity中設定android:screenOrientation="landscape",這樣APP始終保持在橫屏。
  • 在activity中設定android:configChanges="keyboardHidden|screenSize|orientation",這樣activity在“鍵盤隱藏”、“螢幕大小變化”、“橫豎屏切換”的時候,不會產生影響。

一般遊戲開發使用第一種方法,因為遊戲需要一直橫屏。電影播放等使用第二種方法,這樣就可以保持橫豎屏進度條一致啦!

後話

關於Android中的activity中生命週期的理解到此結束了,之後還有對activity的啟動模式的分析!

建議搭配安卓開發文件進行觀看,文章內容僅供參考!

相關文章