首頁 > 軟體

Android Activity被回收的情況分析

2022-11-25 14:00:37

1.onSaveInstanceState()方法

當一個Activity進入了停止狀態,是有可能被系統回收的。想象以下場景:應用中有一個ActivityA,使用者在ActivityA的基礎上啟動了ActivityB,ActivityA就進入了停止狀態,這個時候由於系統記憶體不足,將ActivityA回收掉了,然後使用者按下Back鍵返回ActivityA,會出現什麼情況呢?其實還是會正常顯示ActivityA的,只不過這時並不會執行onRestart()方法,而是會執行ActivityA的onCreate()方法,因為ActivityA在這種情況下會被重新建立一次。

但是這種情況下可能會出現一個重要的問題:ActivityA中是可能存在臨時資料和狀態的。打個比方,MainActivity中如果有一個文字輸入框,現在你輸入了一段文字,然後啟動NormalActivity,這時MainActivity由於系統記憶體不足被回收掉,過了一會你又點選了Back鍵回到MainActivity,這個時候你會發現剛剛輸入的文字都沒了,因為MainActivity被重新建立了。

如果我們的應用出現了這種情況是比較影響使用者體驗的,其實Activity還提供了一個onSaveInstanceState()回撥方法,這個方法可以保證在Activity被回收之前一定會被呼叫,因此我們可以通過這個方法來解決這個問題。

onSaveInstanceState()方法會攜帶一個Bundle型別的引數,Bundle提供了一系列的方法儲存資料,比如可以使用putString()方法儲存字串,使用putInt()方法儲存整型資料,以此類推。每個儲存方法需要傳入兩個引數,第一個引數是鍵,用於後面從Bundle中取值,第二個引數是真正要儲存的內容。

在MainActivity中新增如下程式碼就可以將臨時資料進行儲存了:

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val tempData="Something you just typed"
        outState.putString("data_key",tempData)
    }

資料是已經儲存下來了,那麼我們應該在哪裡進行恢復呢?其實我們一直在使用的onCreate()方法其實也有一個Bundle型別的引數。這個引數在一般情況下都是null,但是如果在Activity被系統回收之前,你通過onSaveInstanceState()方法儲存資料,這個引數就會帶有之前儲存的全部資料,我們只需要再通過相應的取值方法將資料取出即可。

修改MainActivity的onCreate()方法,如下所示:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        if(savedInstanceState!=null){
            val tempData = savedInstanceState.getString("data_key")
            tempData?.let { Log.d("tag", it) }
        }
}

取出值之後就可以再做相應的恢復操作就可以了,比如將文字內容重新賦值到文字輸入框上,這裡我只是簡單列印一下。

這裡使用Bundle儲存和取出資料和我們之前使用Intent傳遞資料的方法很類似,首先我們可以把需要傳遞的資料都儲存在Bundle物件中,然後再將Bundle物件存放在Intent裡。到了目標Activity之後,先從Intent中取出Bundle,再從Bundle中一一取出資料。

另外在手機的螢幕發生旋轉的時候,Activity也會經歷一個重新建立的過程,因而在這種情況下,Activity中的資料也會丟失。這種問題也可以通過onSaveInstanceState()方法來解決,但是對於橫豎屏已經有了更好的方案。

2.ViewModel

使用 ViewModel,我們就無需再用這種方法儲存,因為 ViewModel 會自動感知生命週期,處理資料的儲存與恢復。即資料可在發生螢幕旋轉等設定(其它例如解析度調整、許可權變更、系統字型樣式、語言變更等)更改後繼續留存。

程式碼如下:

package com.example.viewmodeldemo;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    private MyViewModel mMyViewModel;
    private TextView textView;
    private Button mButton1;
    private Button mButton2;
    private final String TAG="MainActivityTest";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
        //建立一個ViewModel物件
        mMyViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
        textView=findViewById(R.id.textView);
        //ViewModel會儲存資料,當你重新建立的時候會載入顯示出來
        textView.setText(String.valueOf(mMyViewModel.number));
        mButton1=findViewById(R.id.button1);
        mButton2=findViewById(R.id.button2);
        mButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mMyViewModel.number++;
                textView.setText(String.valueOf(mMyViewModel.number));
            }
        });
        mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mMyViewModel.number+=2;
                textView.setText(String.valueOf(mMyViewModel.number));
            }
        });
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: ");
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }
}
package com.example.viewmodeldemo;
import androidx.lifecycle.ViewModel;
//這裡的ViewModel可以看作全域性變數倉庫
public class MyViewModel extends ViewModel {
    public int number=0;
}

這樣當你旋轉螢幕生命週期發生變化,你的資料還在。

到此這篇關於Android Activity被回收的情況分析的文章就介紹到這了,更多相關Android Activity被回收內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com