Android之ViewModel

原文請參考官方文檔

 

ViewModel

ViewModel類設計用來存儲和管理與UI相關的數據。這樣數據就可以在配置更改中保存,比如屏幕旋轉。

注:如何添加ViewModel依賴可以查看Android 結構組件之Adding Components to your Project

Android框架管理着ActivityFragment的生命週期。框架可能會根據一些完全超出您控制的用戶操作或設備事件來決定銷燬或重新創建它們。

如果系統銷燬或者重創建UI控制器,那麼存儲在其中的任意UI相關的數據都將丟失。例如,如果你在Activity中有一組用戶列表,當因爲配置改變而被重新創建後,新的Activity必定會重新去獲取用戶列表。對於簡單的數據,Activity可以使用onSaveInstanceState()方法在存儲數據,並且可以在onCreate()方法的bundle參數中恢復。但是這種方法只適用於像UI狀態這樣的少量數據,而不是像用戶列表那樣的潛在的大量數據。

另一個問題是,這些控制器(activity和fragment)經常需要進行一些異步調用,這些調用可能需要一些時間才能返回結果。UI控制器需要管理這些調用,並在銷燬時清除它們,以避免潛在的內存泄漏。這需要大量的維護,並且在爲配置更改重新創建對象的情況下,由於需要重新發出相同的調用,這是對資源的浪費。

最後但同樣重要的是,UI控制器(如Activity和Fragment)主要用於顯示UI數據、對用戶行爲作出反應或處理操作系統通信,例如權限請求。要求UI控制器還負責從數據庫或網絡加載數據,增加了對類的膨脹。對UI控制器分配過多的責任可能導致單個類試圖獨自處理一個應用程序的所有工作,而不是將工作委託給其他類。以這種方式向UI控制器分配過度的責任也會使測試變得更加困難。

實現一個ViewModel

視圖數據所有權UI控制器邏輯分離是更容易、更高效的。Lifecycle提供了一個新的叫做ViewModel的類。它是UI控制器的助手類,負責爲UI準備數據。在配置更改期間,ViewModel會自動保留,這樣它所保存的數據就會立即被下一個ActivityFragment實例所使用。在我們上面提到的例子中,應該是ViewModel負責獲取和保存用戶列表的責任,而不是FragmentActivity

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
    }
}

現在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所創建的相同的MyViewModel實例,當Activity被執行了finish()以後,框架會調用ViewModelonCleared()方法,這樣就可以清除一些資源。

注:由ViewModelActivityFragment的實例要活的長,它不應該引用一個View,或者任何一個可能持有Activity的上下文引用的類

如果ViewModel需要引用Application的上下文(如,開啓系統的服務),它可以繼承AndroidViewModel類(是 ViewModel 的子類),此類有一個構造器接收Application(因爲Application類繼承自Context).

ViewModel的設計是超出LifecycleOwen生命週期的,這個設計還意味着您可以編寫測試來更容易地覆蓋ViewModel,因爲它知道關於視圖和生命週期對象。ViewModel能夠包含LifecycleObservers,例如LiveData對象。然而,ViewModel對象絕不能對生命週期敏感的可觀察對象(如LiveData對象)做更改。

ViewModel的生命週期

ViewModel對象在獲取視圖模型時,將範圍限定爲傳遞給ViewModelProvider的生命週期。ViewModel保留在內存中,直到它的作用域永久消失:例如當Activity 被finish或者當Fragment被detached。
下圖說明的了一個Activity的不同的生命狀態,例如它經過設備旋轉並被銷燬。插圖還顯示了與關聯的Activity生命週期相鄰的ViewModel的生命週期。這個圖表說明了一個活動的狀態,相同的基本狀態適用於Fragment的生命週期。

viewmodel-lifecycle.png

通常,在系統調用活動對象的onCreate()方法時,您通常會請求一個ViewModel。系統可以在活動的整個生命週期中多次調用onCreate(),比如當設備屏幕旋轉時。ViewModel存在於您第一次請求ViewModel時,直到活動完成並銷燬爲止

在多個Fragment間共享數據

Activity中的多個Fragment需要通信這是很常見的。想象一個常見的主細節片段,其中有一個Fragment,用戶從列表中選擇一個項目,另一個Fragment顯示所選項目的內容。這絕不是瑣碎的因爲兩個Fragmengt需要定義一些接口描述,而Activity必須要將他們捆綁在一起才行。此外,兩個Fragment都必須處理另一個尚未創建或不可見的情況。

這中常見的痛點可以通過使用ViewModel對象來解決。設想一個常見的主-從Fragment,其中有一個Fragment用戶從一個列表中選擇一項,而另一個Fragment來顯示所選項的內容。
這些Fragment可以使用它們的Activity範圍域共享一個ViewModel來處理這種通信

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 Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

注意兩個Fragment通過使用getActivity()方法來獲取ViewModelProviders,這意味着它們都將接收相同的SharedViewModel實例,該實例的作用域爲Activity

這種方法的好處包括:

  • Activity不需要做任何事也不需要知道關於通信的任何信息。
  • 除了SharedViewModel契約之外,Fragmengt不需要相互瞭解。如果其中一個消失了另一個就像平常一樣繼續工作。
  • 每個Fragment都有自己的生命週期,並且不受另一個生命週期的影響。事實上在UI中一個Fragment替換另一個Fragment,UI工作也沒有任何問題。

用ViewModel替換Loaders

像CursorLoader這樣的Loader類經常被用來在一個應用程序的UI中保存數據與數據庫同步。你也可以使用ViewModel或者其他的類來替換Loader,使用ViewModel將UI控制器與數據加載操作分開,這意味着類之間的強引用更少。
在使用Loader的的一種常見方法,應用程序可能會使用CursorLoader來觀察數據庫的內容。當數據庫中的值發生變化時,加載器將自動觸發數據的重新加載並更新UI:

viewmodel-loader.png


ViewModel使用RoomLiveData來替換加載器(loader)

  • ViewModel確保數據在設備配置更改中得以保存。
  • 當數據庫發生變化時,Room通知您的LiveData,
  • LiveData反過來用修改後的數據更新UI。

viewmodel-replace-loader.png


這篇博客描述瞭如何使用帶有LiveDataViewModel去替換AsyncTaskLoader
隨着您的數據變得越來越複雜,您可能會選擇一個單獨的類來加載數據。ViewModel的目的是封裝UI控制器的數據,以便在配置更改時讓數據存活。有關如何跨配置更改加載、保存和管理數據的信息,請參閱UI狀態保存

 




鏈接:https://www.jianshu.com/p/9c4bc63eb905

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章