Android架構組件官方文檔03——ViewModel

ViewModel概述

ViewModel類旨在以一種有生命週期意識的方式存儲和管理與UI相關的數據。
ViewModel類容許數據在配置變化(例如屏幕旋轉)後存活。
注意:要將ViewModel導入到Android項目中,請參閱向項目添加組件html

Android framework管理UI控制器的生命週期,例如Activity和Fragment。
framework可能會決定銷燬或從新建立UI控制器,以響應徹底不受您控制的特定用戶操做或設備事件。android

若是系統銷燬或從新建立UI控制器,則存儲在其中的任何臨時UI相關的數據都將丟失。例如,您的應用可能包含其中一項活動中的用戶列表。當爲配置更改從新建立活動時,新活動必須從新獲取用戶列表。
對於簡單的數據,Activity可使用onSaveInstanceState()方法並從onCreate()中的bundle中恢復其數據,但此方法僅適用於能夠序列化而後反序列化的少許數據,可能不適合像用戶或位圖的列表這樣的大量數據。數據庫

另外一個問題是UI控制器常常須要進行異步調用,這可能須要一些時間才能返回。UI控制器須要管理這些調用,並確保系統在銷燬後清理它們以免潛在的內存泄漏。這種管理須要大量的維護,而且在爲配置更改而從新建立對象的狀況下,因爲對象可能不得不從新發出已經作出的請求,因此浪費資源。網絡

UI控制器(如Activity和Fragment)主要用於顯示UI數據,對用戶操做作出反應或處理操做系統通訊(如權限請求)。若是要求UI控制器也負責從數據庫或網絡加載數據,就會使改類變得臃腫。爲UI控制器分配過多的責任可能會致使一個類嘗試單獨處理應用程序的全部工做,而不是將工做委託給其餘類。經過這種方式給UI控制器分配過多的責任也使測試變得更加困難。架構

將視圖數據全部權從UI控制器邏輯中分離出來更簡單,更高效。app

實現一個ViewModel

架構組件爲UI控制器提供ViewModel助手類。ViewModel對象在配置更改期間會自動保留,以便它們保存的數據當即可用於下一個Activity或fragment實例。例如,若是您須要在應用中顯示用戶列表,請明確分配職責來獲取數據並將用戶列表保存到ViewModel,而不是Activity或fragment,如如下示例代碼所示:框架

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

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

而後你能夠從一個Activity中訪問列表,以下所示:異步

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

若是Activity從新建立,它將接收由第一個Activity建立的相同的MyViewModel實例。當持有ViewModel的Activity finish後,框架將調用ViewModel對象的onCleared()方法,以便它能夠清理資源。async

警告:ViewModel毫不能引用視圖,生命週期或可能持有對活動上下文的引用的任何類。ide

ViewModel對象被設計爲脫離視圖或LifecycleOwners的特定實例。這種設計還意味着您能夠更輕鬆地編寫測試來覆蓋ViewModel,由於它不知道視圖和生命週期對象。ViewModel對象能夠包含LifecycleObservers,例如LiveData對象。可是,ViewModel對象毫不能觀察對生命週期感知的可觀察對象(如LiveData對象)的更改。若是ViewModel須要應用程序上下文(例如查找系統服務),那麼它能夠擴展AndroidViewModel類並具備構造函數,該構造函數在構造函數中接收Application,由於Application類擴展了Context。

ViewMode的生命週期

ViewModel對象的範圍是在獲取ViewModel時傳遞給ViewModelProvider的生命週期。ViewModel保留在內存中,直到生命週期的範圍永久消失:在一個Activity的狀況下,finish()時,在一個Fragment的狀況下,當它被detached(分離)時。

圖1說明了一個Activity在進行一次旋轉而後finish後的各類生命週期狀態。該圖還顯示了相關Activity生命週期旁邊ViewModel的生命週期。這個特定的圖表說明了一個Activity的狀態。這些相同的基本狀態一樣適用於Fragment的生命週期。
圖片描述

系統首次調用Activity對象的onCreate()方法時,一般會請求ViewModel。系統可能會在整個Activity的生命週期中屢次調用onCreate(),例如當設備屏幕旋轉時。ViewModel從第一次請求ViewModel直到Activity finished 和銷燬時一直存在。

在片斷之間共享數據

Activity中的兩個或更多fragment須要彼此進行通訊是很常見的。想象一下,主-從關係的F讓給met的一種常見狀況,其中有一個Fragment,用戶從列表中選擇一個項目,另外一個fragment顯示所選項目的內容。這種狀況有些麻煩,由於這兩個片斷都須要定義一些接口描述,而且全部者Activity必須將二者綁定在一塊兒。此外,這兩個fragment必須處理其餘fragment還沒有建立或可見的場景。

可使用ViewModel對象解決這個常見的痛點。這些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.
        });
    }
}

請注意,在獲取ViewModelProvider時,這兩個Fragment都使用getActivity()。所以,兩個Fragment 都接收相同的SharedViewModel實例,該實例的範圍限定爲Activity。
這種方法具備如下優勢:

  • Activity不須要作任何事情,也不須要了解這種溝通。
  • 除了SharedViewModel約定以外,fragment不須要彼此瞭解。若是其中一個fragment消失,另外一個fragment繼續照常工做。
  • 每一個片fragment都有其本身的生命週期,而且不受其餘生命週期的影響。若是一個fragment替換另外一個fragment,UI將繼續工做而不會出現任何問題。

用ViewModel替換Loaders

CursorLoader這樣的Loader類常常用於保持應用程序UI中的數據與數據庫同步。您可使用ViewModel和其餘幾個類來替換Loaders。使用ViewModel將您的UI控制器與數據加載操做分開,這意味着您在類之間的強引用減小了。

在使用loaders的一種常見方法中,應用程序可能使用CursorLoader來觀察數據庫的內容。當數據庫中的值發生更改時,加載程序會自動觸發從新加載數據並更新UI:
圖片描述
圖2.使用加載器加載數據

ViewModel與Room和LiveData一塊兒使用來替換Loaders。ViewModel可確保數據在設備配置更改後仍然存在。當數據庫發生更改時,Room會通知您的LiveData,而LiveData則會用修改的數據更新您的UI。
圖片描述
圖3.使用ViewModel加載數據

此博客文章描述瞭如何將ViewModel與LiveData一塊兒使用來替換AsyncTaskLoader

隨着你的數據變得愈來愈複雜,你可能會選擇一個單獨的類來加載數據。ViewModel的目的是封裝UI控制器的數據,以使數據不受配置更改的影響。有關如何跨配置更改加載,保留和管理數據的信息,請參閱保存UI狀態

Android App Architecture指南建議構建一個存儲庫類來處理這些功能。

相關文章
相關標籤/搜索