【翻譯】安卓架構元件(5)-ViewModel

weixin_33866037發表於2017-06-07

相關文章:

ViewModel被設計用於儲存並管理UI相關的資料,因此可以在配置變化時存活下來,例如當螢幕旋轉的時候。

app元件,例如ActivityFragment擁有可以被安卓框架所管理的生命週期。框架可以決定銷燬或建立它們,這是基於一些使用者的行為或者裝置的事件,而這一切都在你的控制範圍外。

因為一些物件可能會被作業系統銷燬或重新建立,任何你所持有的資料都會丟失。例如,你的Activity擁有一組使用者列表,當Activity由於配置變化而重新建立時,新的Activity不得不重新獲取使用者列表。對於簡單的資料,Activity可以使用onSaveInstanceState()方法並從onCreate()bundle裡恢復資料,但是這種方法僅僅適合於少量資料,例如UI狀態,不適用於大量資料,例如一組使用者列表。

另一個問題是,這些UI控制器(ActivityFragment等)需要頻繁地非同步呼叫,可能會需要一些時間返回。UI控制器需要管理這些呼叫,並當被銷燬的時候清除它們,以避免潛在的記憶體洩漏。這需要很多的程式碼維護,並且在由於配置變化而重新建立的情況下是很浪費資源的,因為需要重新進行相同的呼叫。

另外,這些UI控制器已經響應使用者行為或處理作業系統互動。當它們也需要手動處理資源的時候,會使得類急速膨脹。上帝的Activity或上帝的Fragment,是指一個試圖處理所有app工作的單獨類,而不是分派到其他類去完成。這樣也會使得測試工作很難進行。

將我們的UI和控制邏輯分離變得越來越容易和高效了。Lifecycle提供了一個新類叫做ViewModel,一個負責為UI準備資料的幫助類。ViewModel會在配置發生變化的時候自動儲存,所以其所持有的資料會在新的Activity或新的Fragment立即可用。在上面我們所提及的例子中,應當是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() {
        // 非同步呼叫獲取使用者列表
    }
}

現在新的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終止後,框架呼叫ViewModelonCleared()方法清除資源。

因為ViewModel在指定的ActivityFragment例項外存活,它應該永遠不能引用一個View,或持有任何包含Activity context引用的類。如果ViewModel需要Application的context(如獲取系統服務),可以擴充套件AndroidViewmodel,並擁有一個構造器接收Application

Fragment間共享資料

一個Activity中的多個Fragment相互通訊是很常見的。每個Fragment需要定義介面描述,所屬Activity將二者捆綁在一起。此外,每個Fragment必須處理其他Fragment未建立或不可見的情況

通過使用ViewModel可以解決這個痛點。想象一種情況,一個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 ->
           // update UI
        });
    }
}

請注意,兩個Fragment都使用了getActivity(),以通過ViewModelProviders獲取SharedViewModel

這種方式的好處包括:

  • Activity不需要做任何事情,也不需要知道通訊的事情
  • Fragment不需要知道彼此,除了SharedViewModel進行聯絡。如果它們(Fragment)其中一個消失了,其餘的仍然能夠像往常一樣工作
  • 每個Fragment有自己的生命週期,而且不會受其它Fragment生命週期的影響。事實上,一個Fragment替換另一個Fragment,UI的工作也不會受到任何影響。

ViewModel的生命週期

ViewModel物件的範圍由獲取ViewModel時傳遞至ViewModelProviderLifecycle所決定。ViewModel始終處在記憶體中,直到Lifecycle永久地離開—對於Activity來說,是當它終止(finish)的時候,對於Fragment來說,是當它分離(detached)的時候。

1226129-600087fd745d03e7.png
image

相關文章