【Android】Android四大組件之Activity(二)

【Android】Android四大組件之Activity(二)

前言

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

1、什麼是生命周期?

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

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

首先,給出Android開發文檔中對生命周期的介紹:了解 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()存儲的數據

我們可以看看Android開發文檔中給出的例子:

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的啟動模式的分析!

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

Tags: