【譯】Google官方推出的Android架構組件系列文章(五)ViewModel

系列文章導航

  1. 【譯】Google官方推出的Android架構組件系列文章(一)App架構指南
  2. 【譯】Google官方推出的Android架構組件系列文章(二)將Architecture Components引入工程
  3. 【譯】Google官方推出的Android架構組件系列文章(三)處理生命周期
  4. 【譯】Google官方推出的Android架構組件系列文章(四)LiveData
  5. 【譯】Google官方推出的Android架構組件系列文章(五)ViewModel
  6. 【譯】Google官方推出的Android架構組件系列文章(六)Room持久化庫

原文地址:https://developer.android.com/topic/libraries/architecture/viewmodel.html

ViewModel類被設計用來存儲和管理UI相關數(shù)據(jù),以便數(shù)據(jù)能在配置更改(比如屏幕旋轉(zhuǎn))中生存下來。

應用組件,比如activity和fragment,具有由Android框架管理的生命周期。框架可以根據(jù)用戶行為或完全不由你控制的設備事件來決定銷毀還是重建他們。

因為這些對象可能會被操作系統(tǒng)銷毀或重建,任何你持有的這些組件中的數(shù)據(jù)都會丟失。比如,如果在activity中有一個用戶列表,當由于配置更改而引起的activity重建時,新的activity不得不再次拉取用戶列表。對于簡單的數(shù)據(jù),activity可以使用onSaveInstanceState()方法,然后從onCreate()bundle中恢復數(shù)據(jù),但這種方法僅僅適用于少量數(shù)據(jù),比如UI狀態(tài),對于潛在的大量數(shù)據(jù),比如用戶列表,則不適用。

另外一個問題是,這些UI控制器(activity,fragment等等)經(jīng)常需要做一些需要花費一定時間才能返回的異步調(diào)用。他們需要管理這些調(diào)用,當其銷毀時清理它們,防止內(nèi)存泄漏。這需要大量維護工作,并且當對象由于配置更改重建時,這是浪費資源的,因為它需要發(fā)出相同的調(diào)用。

最后但并非不重要的是,這些UI控制器已經(jīng)需要響應用戶操作或者處理操作系統(tǒng)通信。當他們還需要手動處理其資源時,將使得類膨脹,創(chuàng)造“上帝activity”(或“上帝fragment”),也就是說,采用一個單獨的類來試圖自己處理所有的應用程序工作,而不是將工作委托給其他類。這也使得測試變得更改困難。

將視圖數(shù)據(jù)所有權從UI控制器邏輯分離是更容易和更高效的。Lifecycle提供了一個名為ViewModel的新類,一個負責為UI準備數(shù)據(jù)的輔助類。在配置改變時,ViewModel會自動保留,這樣一來,它持有的數(shù)據(jù)能夠立即對下一個activityfragment實例可用。在我們上面提到的例子里,獲取以及保存用戶列表數(shù)據(jù)的責任應該是ViewModel的,而不是activityfragment。

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // do async operation to fetch users
    }
}

現(xiàn)在activity可以像下面這樣訪問用戶列表:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果activity重建,它將接收到由上個activity創(chuàng)建的同一個MyViewModel實例。當ViewModel的擁有者activity結束時,框架調(diào)用ViewModelonCleared()方法,以便它可以清理資源。

注意 :因為ViewModel的生命周期超出了具體的activity和fragment實例,所以它不應該引用任何View或任何可能持有activity context引用的類。

Fragment間共享數(shù)據(jù)

一個activity中的兩個或多個activity需要互相通信的情況是很常見的。這并非微不足道,因為兩個fragment需要定義一些接口描述,并且其所有者activity必須將兩者綁定到一起哦。此外,兩個fragment必須處理其他fragment還沒創(chuàng)建或不可見的情況。

通過使用ViewModel對象可以解決這個常見的痛點。假設一個常見的主-詳細fragment場景:一個包含用戶可以選擇項的列表fragment,另一個fragment用來展示選擇項的內(nèi)容。

這些fragment共享一個ViewModel,使用他們的activity范圍來處理這種通信。

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends LifecycleFragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // update UI
        });
    }
}

請注意,兩個fragment在獲取ViewModelProvider時都調(diào)用了getActivity()。這意味著,他們兩個將會收到同一個SharedViewModel實例,這個實例的作用域是其activity。

這種方式的好處包括:

  • activity不需要做和知道任何關于這次通信的事情。
  • fragment不需要互相知道除了SharedViewModel約束。如果其中一個消失,另一個都能正常工作。
  • 每個fragment有自己的生命周期,不受其他fragment生命周期的影響。實際上,
    在一個fragment替換另一個fragment的UI中,UI可以正常工作,沒有任何問題。

ViewModel的生命周期

在獲取ViewModel時,ViewModel對象的作用域綁定到傳遞給ViewModelProviderLifecycle。ViewModel保留在內(nèi)存中,直到其綁定的Lifecycle永久消失——如果是activity,則是其finish時,如果是fragment,則是其detached時。

viewmodel-lifecycle.png

ViewModel vs SavedInstanceState

ViewModel提供了一種方便的方法在配置更改之間保留數(shù)據(jù),但是如果應用程序被操作系統(tǒng)殺死,則他們不會保留。

比如,如果用戶離開應用,然后幾個小時后回來,進程在那個時候已經(jīng)被殺掉,Android系統(tǒng)將從保存的狀態(tài)里面還原Activity。所有框架組件(view,acitivty,fragment)使用保存實例狀態(tài)機制來保存他們的狀態(tài),因此大部分時間,你啥也不用做。你可以使用onSaveInstanceState回調(diào)將自定義數(shù)據(jù)加入到bundle中。

通過onSaveInstanceState保存的數(shù)據(jù)是保留在系統(tǒng)進程內(nèi)存中,Android系統(tǒng)允許你僅僅保留一個小塊數(shù)據(jù),因此這不是保留你的應用實際數(shù)據(jù)的好地方。你應該謹慎使用它來存放哪些不容易被UI組件表示的東西。

舉個例子,如果有一個展示國家信息的用戶界面,你永遠也不要把Country對象放到保存實例狀態(tài)中。你可以把countryId放到里面(除非它已經(jīng)由View或Fragment參數(shù)保存)。實際的對象應該保存在數(shù)據(jù)庫中,ViewModel可以通過保存的countryId獲取。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容