[譯] Architecture Components 之 ViewModel

zly394發表於2017-06-11

【目錄】

1. Architecture Components 之 Guide to App Architecture

2. Architecture Components 之 Adding Components to your Project

3. Architecture Components 之 Handling Lifecycles

4. Architecture Components 之 LiveData

5. Architecture Components 之 ViewModel

6. Architecture Components 之 Room Persistence Library

示例程式碼連結


ViewModel

ViewModel 類是被設計用來儲存和管理 UI 相關的資料,以便在配置更改(如:螢幕旋轉)時資料可以保留下來。

注:在 Android 專案中匯入 ViewModel,請參閱新增元件到專案中

應用程式元件(如:activity 和 fragment)具有一個由 Android Framework 管理的生命週期。Framework 可能會完全不受控制的根據某些使用者操作或裝置事件來決定銷燬或重新建立它們。

由於這些物件有可能被作業系統銷燬或重新建立,所以儲存在它們中的任何資料都會丟失。例如:如果 activity 中有一個使用者列表,當 activity 因為配置更改而重新建立時,新的 activity 必須重新獲取使用者列表。對於簡單的資料,activity 可以使用 onSaveInstanceState() 方法從 onCreate() 中的 bundle 裡恢復資料,但是這種方式只適用於少量資料(如:UI 狀態),不適用於大量資料(如:使用者列表)。

另一個問題是,這些 UI 控制器(activity,fragment 等)經常需要發起一些需要一定時間才能返回的非同步呼叫。UI 控制器需要管理這些呼叫並且在其被銷燬時清理它們避免潛在的記憶體洩漏。這需要大量的維護工作,並且在由於配置更改導致物件被重新建立的情況下十分浪費資源,因為需要重新發起相同的請求。

最後單也很重要的是,這些 UI 控制器已經需要完成響應使用者操作和處理作業系統通訊的工作了。當它們還需要手動處理其資源時,將會使類變的臃腫,創造“萬能 activity”(或“萬能 fragment”);也就是說,一個單獨的類檢視自己處理應用程式的所有工作,而不是將工作委派給其它類。這將會使測試非常困難。

將檢視資料的所有權從 UI 控制器的邏輯中分離出來是簡單高效的。Lifecycle 提供了一個叫 ViewModel 的新類,一個 UI 控制器的幫助類,用來為 UI 準備資料。在配置更改期間,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<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // 執行非同步操作獲取使用者
    }
}複製程式碼

現在 activity 可以像下面這樣訪問列表:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // 更新 UI
        });
    }
}複製程式碼

如果 activity 被重新建立,它將會收到由之前 activity 建立的同一個 MyViewModel 例項。當所有者 activity 結束後,Framework 會呼叫 ViewModelonCleared() 方法來清理資源。

注:由於 ViewModel 存活的比個別的 activity 和 fragment 例項,所以它決不能引用 View,或任何持有 activity context 的引用的類。如果 ViewModel 需要 Application 的 context(如:呼叫系統服務),可以繼承 AndroidViewModel 類,可以在建構函式中接受 Application(因為 Application 繼承了 Context)。

在 Fragment 之間共享資料

activity 中的兩個或多個 fragment 需要相互通訊是很常見的。這不是個簡單的事情,所有的 fragment 都需要定義一些介面秒素,並且擁有它們的 activity 必須將兩者繫結在一起。另外,所有的 fragment 必須處理其它的 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 onActivityCreated() {
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends LifecycleFragment {
    public void onActivityCreated() {
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // 更新 UI
        });
    }
}複製程式碼

請注意,在獲取 ViewModelProvider 時兩個 fragment 都使用 getActivity() 方法。這意味著它們都將會收到被 activity 限定的同一個 SharedViewModel 例項。

這種方式的優點有:

  • activity 不需要做或知道關於該通訊的任何事情。

  • 除了 SharedViewModel 協議之外 fragment 不需要了解彼此。如果其中一個消失,另一個會照常工作。

  • 每個 fragment 有自己的生命週期,並且不受其它 fragment 的生命週期影響。實際上,在 UI 中一個 fragment 替換另一個 fragment,UI 的執行沒有任何問題。

ViewModel 的生命週期

在獲取 ViewModel 時,ViewModel 物件被傳遞給 ViewModelProviderLifecycle 限定。ViewModel 保留在記憶體中,直到限定它的 Lifecycle 永久消失(當 activity 結束或 fragment 被分離)。

[譯] Architecture Components 之 ViewModel

相關文章