Android – 翻轉裝置時的生命週期處理

當 Activity 的生命週期處在執行中的狀態時, 當行動裝置翻轉(從直向轉成橫向, 或是橫向轉成直向)的時候, 將會發現原來的 Activity 呼叫了:

  1. onPause()
  2. onStop()
  3. onDestroy()

 

緊接著:

  1. onCreate()
  2. 再度進入到執行中的狀態

 

事實上, 再來從 Activity 的物件建構式的角度來觀看, 已經重新建立另一個 Activity 的物件實體, 並啟動該新的生命週期. 這樣對於 app 的開發上, 相當於行動裝置翻轉之後, 必須重新啟動新的一個 Activity, 從使用者經驗的角度來看, 許多場合並不適用, 而會是期望能保留上一個狀態, 繼續執行.

此時, 在結束生命週期之前, 有一個 onSaveInstanceState(Bundle outState) 將會被呼叫, 其作用就是在離開之前, 可以先將狀態進行儲存, 存放在傳遞的參數物件 Bundle 中. 而該 Bundle 物件, 將會在啟動新的  Activity 執行了 onCreate(Bundle savedInstanceState) 的時候, 傳遞的參數就是上一次的 Bundle 物件實體.

或是, 等 onCreate() 執行之後, 會繼續呼叫執行 onRestoreInstanceState(Bundle savedInstanceState), 傳遞的參數也是相同一個的 Bundle 的物件實體. 來進行復原的事情, 其實通常大多是針對 View 來處理.

先來設計一個簡單的UI:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="tw.brad.activityonsavedinstancestate.MainActivity">

    <TextView
        android:id="@+id/main_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="test1"
        android:text="test1"
        />
</LinearLayout>

有一個 Button 與一個 TextView.

當使用者按下 Button, 觸發 test1() 方法, 產生樂透數字呈現在 TextView.

public void test1(View view) {
    textView.setText("" + (int)(Math.random()*49+1));

    timer.purge();
    task = new MyTask();
    timer.schedule(task, 0, 500);

}

這是一個非常簡單的程式, 但是當行動裝置翻轉之後, 產生在 TextView 上的數字將會不在, 而恢復到呈現最初始的Hello World!

因此, 將在 onSaveInstanceState() 中先進行儲存:

// 進行儲存的動作, 在 onStop() 之前
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putString("textView", textView.getText().toString());

}

 

然後, 有兩個處理方式: (擇一處理即可)

onCreate

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

    textView = findViewById(R.id.main_tv);

    if (savedInstanceState != null) {
        String tv = savedInstanceState.getString("textView");
        textView.setText(tv);
    }
    
}

或是

onRestoreInstanceState

// 進行復原的工作, 在 onCreate() 之後
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    String tv = savedInstanceState.getString("textView");
    textView.setText(tv);

}

 

兩者的差異, 就是 onCreate() 中要判斷 Bundle 物件實體是否為 null, 因為任何 Activity 最一開始被啟動時, 該 Bundle 為 null, 而且不會去執行 onRestoreInstanceState(), 所以, 在 onRestoreInstanceState() 中可以不需要判斷.

如果不是 View 的狀態要儲存的話, 可以使用 static 成員變數來處理即可.

例如有一個 Timer, 會持續的週期運作執行, 當翻轉的時候, 不希望從頭開始, 而 Activity 結束前, 亦能結束 Timer 的週期任務.

如下:

package tw.brad.activityonsavedinstancestate;

import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private static Timer timer = new Timer();
    private static MyTask task;
    private static int i = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("brad", "onCreate");

        textView = findViewById(R.id.main_tv);

//        if (savedInstanceState != null) {
//            String tv = savedInstanceState.getString("textView");
//            textView.setText(tv);
//        }

    }

    // 進行復原的工作, 在 onCreate() 之後
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        String tv = savedInstanceState.getString("textView");
        textView.setText(tv);

    }

    // 進行儲存的動作, 在 onStop() 之前
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.v("brad", "onSave");

        outState.putString("textView", textView.getText().toString());

    }

    public void test1(View view) {
        textView.setText("" + (int)(Math.random()*49+1));

        timer.purge();
        task = new MyTask();
        timer.schedule(task, 0, 500);

    }

    private class MyTask extends TimerTask {
        @Override
        public void run() {
            i++;
            Log.v("brad", "i = " + i);
        }
    }

    @Override
    public void finish() {
        Log.v("brad", "finish");
        if (timer != null){
            timer.cancel();
            timer = null;
        }
        super.finish();
    }
}

 

 

 

 

 

 

 

 

本站資源一切隨緣,
不用註冊, 不看廣告
如果對您有所助益,
歡迎功德隨喜, 金額隨意,
請點擊以下...(感謝您)

功德箱/打賞箱

%d bloggers like this: