首頁>技術>

授權原文:https://mp.weixin.qq.com/s/tBojbetY1pmqHoNFYJcK8w

前言

在引入了 Jetpack 之後,我們通常使用 ViewModel 元件來管理資料,當頁面因配置變更(尤其是在發生像旋轉這樣頻繁的配置更改之後)而重建時,可以使用 ViewModel 和 onSaveInstanceState(),以確保應用滿足使用者對其介面狀態的預期。

參考:Jetpack-VM再不懂你就out了

但如果是記憶體不足或者電量不足等系統原因導致的頁面被回收時 ViewModel 是不會被複用的。

一、使用者預期和系統行為

參考官網 :https://developer.android.google.cn/topic/libraries/architecture/saving-states

1、使用者發起的介面狀態解除

使用者希望當他們啟動 Activity 時,該 Activity 的暫時性介面狀態會保持不變,直到使用者完全關閉 Activity 為止。使用者可以透過以下方式完全關閉 Activity:

按返回按鈕從“概覽”(“最近使用的應用”)螢幕中滑動關閉 Activity從 Activity 向上導航從“設定”螢幕中終止應用完成某種“完成”Activity(由 Activity.finish() 提供支援)

以上情況 Activity 例項將連同其中儲存的任何狀態以及與該 Activity 關聯的任何已儲存例項狀態記錄一起被銷燬並從記憶體中移除。

2、系統發起的介面狀態解除

使用者期望 Activity 的介面狀態在整個配置變更(例如旋轉或切換到多視窗模式)期間保持不變。

但是,預設情況下,系統會在發生此類配置更改時銷燬 Activity;

例如應用切換至後臺後重新回到App時記憶體不足導致App被重啟;

我們知道Activity可以透過 onSaveInstanceState() onRestoreInstanceState()方式 狀態儲存機制,頁面重建後 ,可以恢復之前的狀態,為使用者提供更好的體驗。

onSaveInstanceState() 回撥會儲存一些資料,如果系統銷燬後又重新建立介面控制器(如 Activity 或 Fragment),則需要使用這些資料重新載入該控制器的狀態。

因為 onSavedInstanceState() 會將資料序列化到磁碟。如果序列化的物件很複雜,序列化會佔用大量的記憶體。

所以onSavedInstanceState() 不能用於儲存大量的資料(如點陣圖),也不能用於儲存冗長複雜資料結構

而是隻能用於儲存基本型別和簡單的小物件,例如字串

二、記憶體不足或者電量不足等系統原因導致的頁面被回收時 ViewModel 不會被複用的情況

模擬購買商品時增加/減少數量的場景:

1、建立ViewModel,新增add()&del()控制num變化
public class MyNormalViewModel extends ViewModel {    private MutableLiveData<Integer> num = new MutableLiveData<Integer>(1);    public LiveData<Integer> getNumber(){        return num;    }    public void add(){        num.setValue((int)num.getValue() + 1);    }    public void del(){        if(num.getValue() > 1){            num.setValue((int)num.getValue() - 1);        }    }}
2、建立Activity,透過DataBinding控制UI資料
public class MyActivity extends AppCompatActivity {    MyNormalViewModel myViewModel;    MainActivityMyBinding binding;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        binding = DataBindingUtil.setContentView(this, R.layout.main_activity_my);        myViewModel = new ViewModelProvider(this).get(MyNormalViewModel.class);        binding.setVm(myViewModel);        binding.setLifecycleOwner(this);    }}
3、建立佈局檢視,透過DataBinding控制檢視屬性
<?xml version="1.0" encoding="utf-8"?><layout xmlns:tools="http://schemas.android.com/tools"    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto">    <data>        <variable            name="vm"            type="com.pxwx.main.viewmodel.MyNormalViewModel" />    </data>    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center">        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="@dimen/dp_30"            android:orientation="horizontal">                <androidx.appcompat.widget.AppCompatButton                android:id="@+id/btnDecrease"                android:layout_width="30dp"                android:layout_height="30dp"                android:gravity="center"                android:text="-"                android:textStyle="bold"                android:background="#999999"                android:onClick="@{()->vm.del()}"/>                <EditText                android:id="@+id/etAmount"                android:layout_width="wrap_content"                android:layout_height="match_parent"                android:minWidth="50dp"                android:background="@null"                android:inputType="number"                android:gravity="center"                android:enabled="false"                android:textColor="@color/color_222222"                android:text="@{vm.number.toString()}"/>                <androidx.appcompat.widget.AppCompatButton                android:id="@+id/btnIncrease"                android:layout_width="30dp"                android:layout_height="30dp"                android:gravity="center"                android:text="+"                android:textStyle="bold"                android:background="#999999"                android:onClick="@{()->vm.add()}"/>        </LinearLayout>        </RelativeLayout></layout>

模擬購買商品時增加/減少數量的場景步驟:

1、增加要兌換的商品數量2、開發者選項開啟【不保留活動】3、按home鍵到桌面後再從程序中回到app4、再進行加減操作時數量是重置狀態了

設定不保留活動

App進入後臺銷燬重新進入程序流程

透過ViewModel該如何解決呢?

應用切換至後臺後重新回到App時記憶體不足導致App被重啟;為了它能夠幫助開發者在 ViewModel 中處理 Activity 和 fragment 狀態儲存和恢復,googole提供了viewmodel-savedstate元件

三、ViewModel-SavedState儲存資料1、首先先新增依賴
api 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
2、ViewModel 搭配 SavedState 實現資料複用

ViewModel 可以有一個接收 SavedStateHandle 的建構函式:

public class MySavedStateViewModel extends ViewModel {    private SavedStateHandle handle;    public static final String KEY = "KEY_NUMBER";    public MySavedStateViewModel(SavedStateHandle handle){        if (!handle.contains(KEY)) {            handle.set(KEY, 0);        }        this.handle = handle;    }    public LiveData<Integer> getNumber(){        return handle.getLiveData(KEY);    }    public void add(){        handle.set(KEY,(int)handle.get(KEY) + 1);    }    public void del(){        handle.set(KEY,(int)handle.get(KEY) - 1);    }}
3、透過傳入savedstateviewmodel建立viewmodel
public class MyActivity extends AppCompatActivity {    MySavedStateViewModel myViewModel;    MainActivityMyBinding binding;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //用databinding來繫結佈局        binding = DataBindingUtil.setContentView(this, R.layout.main_activity_my);        //透過傳入savedstateviewmodel建立viewmodel        myViewModel = new ViewModelProvider(this,new SavedStateViewModelFactory(getApplication(),this)).get(MySavedStateViewModel.class);        binding.setVm(myViewModel);        binding.setLifecycleOwner(this);    }}
4、佈局檔案處理,同上面的列子一樣,不變
<?xml version="1.0" encoding="utf-8"?><layout xmlns:tools="http://schemas.android.com/tools"    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto">    <data>        <variable            name="vm"            type="com.pxwx.main.viewmodel.MySavedStateViewModel" />    </data>    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center">        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="@dimen/dp_30"            android:orientation="horizontal">                <androidx.appcompat.widget.AppCompatButton                android:id="@+id/btnDecrease"                android:layout_width="30dp"                android:layout_height="30dp"                android:gravity="center"                android:text="-"                android:textStyle="bold"                android:background="#999999"                android:onClick="@{()->vm.del()}"/>                <EditText                android:id="@+id/etAmount"                android:layout_width="wrap_content"                android:layout_height="match_parent"                android:minWidth="50dp"                android:background="@null"                android:inputType="number"                android:gravity="center"                android:enabled="false"                android:textColor="@color/color_222222"                android:text="@{vm.number.toString()}"/>                <androidx.appcompat.widget.AppCompatButton                android:id="@+id/btnIncrease"                android:layout_width="30dp"                android:layout_height="30dp"                android:gravity="center"                android:text="+"                android:textStyle="bold"                android:background="#999999"                android:onClick="@{()->vm.add()}"/>        </LinearLayout>        </RelativeLayout></layout>

再次模擬購買商品時增加/減少數量的場景步驟:

1、增加要兌換的商品數量2、開發者選項開啟【不保留活動】3、按home鍵到桌面後再從程序中回到app4、再進行加減操作時數量是在原來儲存的資料基礎上增加(正常)SavedStateHandle 類包含鍵值對對映應有的方法:get(String key)contains(String key)remove(String key)set(String key, T value)keys()

如上使用:此外,還有一種特殊的方法:getLiveData(String key),用於返回封裝在 LiveData 可觀察物件中的值。

當使用Savedstate儲存資料之後,後臺程序關閉,資料也會得到保留

SavedStateHandle生命週期

17
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Jetpack架構元件庫-DataBinding真香