深入了解架構(gòu)組件之ViewModel

官方文檔連接(需翻墻)

一、介紹

ViewModel類是被設(shè)計用來以可感知生命周期的方式存儲和管理 UI 相關(guān)數(shù)據(jù),ViewModel中數(shù)據(jù)會一直存活即使 activity configuration發(fā)生變化,比如橫豎屏切換的時候。

以上是官網(wǎng)的話,就不翻譯了沒啥意思,英語也不好。還是以我的大白話說說吧。
先來看 ViewModel 可以解決那些痛點。

1、數(shù)據(jù)持久化

我們知道在屏幕旋轉(zhuǎn)的 時候 會經(jīng)歷 activity 的銷毀與重新創(chuàng)建,這里就涉及到數(shù)據(jù)保存的問題,顯然重新請求或加載數(shù)據(jù)是不友好的。在 ViewModel 出現(xiàn)之前我們可以用 activity 的onSaveInstanceState()機制保存和恢復(fù)數(shù)據(jù),但缺點很明顯,onSaveInstanceState只適合保存少量的可以被序列化、反序列化的數(shù)據(jù),假如我們需要保存是一個比較大的 bitmap list ,這種機制明顯不合適。
由于 ViewModel 的特殊設(shè)計,可以解決此痛點。
先來看下 ViewModel 生命周期圖:


image.png

由圖可知,ViewModel 生命周期是貫穿整個 activity 生命周期,包括 Activity 因旋轉(zhuǎn)造成的重創(chuàng)建,直到 Activity 真正意義上銷毀后才會結(jié)束。既然如此,用來存放數(shù)據(jù)再好不過了。

2、異步回調(diào)問題

通常我們 app 需要頻繁異步請求數(shù)據(jù),比如調(diào)接口請求服務(wù)器數(shù)據(jù)。當(dāng)然這些請求的回調(diào)都是相當(dāng)耗時的,之前我們在 Activity 或 fragment里接收這些回調(diào)。所以不得不考慮潛在的內(nèi)存泄漏情況,比如 Activity 被銷毀后接口請求才返回。處理這些問題,會給我們增添好多復(fù)雜的工作。
但現(xiàn)在我們利用 ViewModel 處理數(shù)據(jù)回調(diào),可以完美的解決此痛點。

3、分擔(dān) UI controller負(fù)擔(dān)

從最早的 MVC 到目前流行的 MVP、MVVM,目的無非是 明確職責(zé),分離 UI controller 負(fù)擔(dān)。
UI controller 比如 Activity 、Fragment 是設(shè)計用來渲染展示數(shù)據(jù)、響應(yīng)用戶行為、處理系統(tǒng)的某些交互。如果再要求他去負(fù)責(zé)加載網(wǎng)絡(luò)或數(shù)據(jù)庫數(shù)據(jù),會讓其顯得臃腫和難以管理。所以為了簡潔、清爽、絲滑,我們可以分離出數(shù)據(jù)操作的職責(zé)給 ViewModel。

4、Fragments 間共享數(shù)據(jù)

(可以先看下后面的用法介紹)

比如在一個 Activity 里有多個fragment,這fragment 之間需要做某些交互。我之前的做法是接口回調(diào),需要統(tǒng)一在 Activity 里管理,并且不可避免的 fragment 之間還得互相持有對方的引用。仔細(xì)想想就知道這是很翔的一件事,耦合度高不說,還需要大量的容錯判斷(比如對方的 fragment 是否還活著)。

那么用 ViewModel 是怎么樣的呢(官網(wǎng)例子):
(activity 與其內(nèi)部的 fragment 可以共用一個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.
        });
    }
}

仔細(xì)體會下這樣的好處會發(fā)現(xiàn):
1、Activity 不需要做任何事,甚至不知道這次交互,完美解耦。
2、Fragment 只需要 與ViewModel交互,不需要知道對方 Fragment 的狀態(tài)甚至是否存在,更不需要持有其引用。所有當(dāng)對方 Fragment 銷毀時,不影響本身任何工作。
3、Fragment 生命周期互不影響,甚至 fragment 替換成其他的 也不影響這個系統(tǒng)的運作。

二、用法簡介

ViewModel一般配合 LiveData 使用,LiveData可以參考我另一篇文章。
首先,獲取 ViewModel 實例,通過提供的類ViewModelProviders:

 MyViewModel model = ViewModelProviders.of(activity).get(MyViewModel.class);
或
 MyViewModel model = ViewModelProviders.of(fragment).get(MyViewModel.class);

或帶有 Factory 的

 MyViewModel model = ViewModelProviders.of(activity,factory).get(MyViewModel.class);

VM 內(nèi)部操作:

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 觀察數(shù)據(jù)變化:

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

三、源碼分析原理

先從 ViewModel 生命周期開始的時刻著手分析,那么什么時候開始的呢?廢話,當(dāng)然是從我們實例化它開始,那么我們什么時候?qū)嵗?,官網(wǎng)的原話是:
You usually request a ViewModel the first time the system calls an activity object's onCreate() method.
沒錯,我們一般在 onCreate 里初始化。

ViewModel 的出生:

實例化的代碼很簡單,我們慢慢剖析,做了哪些事情

ViewModelProviders.of(activity,factory).get(MyViewModel.class)

1、 首先是ViewModelProviders 的 of 方法:

    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        initializeFactoryIfNeeded(checkApplication(activity));
        return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
    }

    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @NonNull Factory factory) {
        checkApplication(activity);
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

參數(shù)有 activity 與 fragment 的我就只貼 activity 的了 ,重點看這里引出了一個 Factory,不帶Factory的方法只是通過initializeFactoryIfNeeded初始化了一個sDefaultFactory(Factory的實現(xiàn)類):

    /**
     * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
     */
    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

只有一個 create 方法,用腳指頭想想也知道肯定是用來初始化viewmodel的。先放著里等會用到再說。繼續(xù)看 of 方法:

return  new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory)

出現(xiàn)了兩個新的類ViewModelProvider與ViewModelStores,先看ViewModelProvider:

public class ViewModelProvider {

    private static final String DEFAULT_KEY =
            "android.arch.lifecycle.ViewModelProvider.DefaultKey";
 
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    ……
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

}

多余的都省略了,此類很簡單 就是維護了 一個mFactory,一個新出現(xiàn)的類ViewModelStore(待會會講),并提供了用mFactory和ViewModelStore生成 ViewModel 的 get 方法。哇,這里就已經(jīng)看到 ViewModel 最終實例化的地方了,但是別著急還有好多東西呢。

再來看

ViewModelStores.of(activity)

干了啥。先看ViewModelStores:

/**
 * Factory methods for {@link ViewModelStore} class.
 */
@SuppressWarnings("WeakerAccess")
public class ViewModelStores {

    private ViewModelStores() {
    }

    /**
     * Returns the {@link ViewModelStore} of the given activity.
     *
     * @param activity an activity whose {@code ViewModelStore} is requested
     * @return a {@code ViewModelStore}
     */
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        return holderFragmentFor(activity).getViewModelStore();
    }

    /**
     * Returns the {@link ViewModelStore} of the given fragment.
     *
     * @param fragment a fragment whose {@code ViewModelStore} is requested
     * @return a {@code ViewModelStore}
     */
    @MainThread
    public static ViewModelStore of(@NonNull Fragment fragment) {
        return holderFragmentFor(fragment).getViewModelStore();
    }
}

只有兩個 of 方法,holderFragmentFor為 HolderFragment 初始化的靜態(tài)方法, 劇透一下HolderFragment 發(fā)揮了至關(guān)重要的作用,這里先不講其重要作用 只看getViewModelStore()

public class HolderFragment extends Fragment {
……
    private ViewModelStore mViewModelStore = new ViewModelStore();
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }
……
}

沒啥說的,看ViewModelStore:

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.get(key);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
        mMap.put(key, viewModel);
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

很明顯是一個用來存放 ViewModel 實例的類,內(nèi)部維護了一個 HashMap 存放 ViewModel,
并提供了 get,put,clear方法。

至此ViewModelProviders of 做了哪些事情呢:
1、初始化了ViewModelProvider內(nèi)部維護了 用于創(chuàng)建 VM 的 Factory,和用戶存放 VM 的ViewModelStore;
2、初始化了 用來生成 ViewModel 的 Factory(默認(rèn)為DefaultFactory);
3、通過ViewModelStores的靜態(tài)方法實例化了 HolderFragment,并實例化了ViewModelStore

2、然后是ViewModelProvider的 get 方法:

上面已經(jīng)貼過了,再貼一下吧:

ViewModelProviders.of(activity,factory).get(MyViewModel.class);

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

邏輯不復(fù)雜,先看ViewModelStore是不是已經(jīng)存了,沒有的話就通過 factory 實例化, 并存到 ViewModelStore 中。

ViewModel 的死亡:

再來看 ViewModel 是如何結(jié)束其一生的。

public abstract class ViewModel {
    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

Vm 只有一個onCleared方法,那么他是在何時調(diào)用的呢
這里就要介紹之前提到的 HolderFragment了,

public class HolderFragment extends Fragment {
    private static final String LOG_TAG = "ViewModelStores";

    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String HOLDER_TAG =
            "android.arch.lifecycle.state.StateProviderHolderFragment";

    private ViewModelStore mViewModelStore = new ViewModelStore();

    public HolderFragment() {
        setRetainInstance(true);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sHolderFragmentManager.holderFragmentCreated(this);
    }

   @Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }

    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

    static class HolderFragmentManager {
        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
        ……
         private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
            HolderFragment holder = new HolderFragment();
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
            return holder;
        }
         HolderFragment holderFragmentFor(FragmentActivity activity) {
            FragmentManager fm = activity.getSupportFragmentManager();
            HolderFragment holder = findHolderFragment(fm);
            if (holder != null) {
                return holder;
            }
            holder = mNotCommittedActivityHolders.get(activity);
            if (holder != null) {
                return holder;
            }

            if (!mActivityCallbacksIsAdded) {
                mActivityCallbacksIsAdded = true;
                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
            }
            holder = createHolderFragment(fm);
            mNotCommittedActivityHolders.put(activity, holder);
            return holder;
        }
  ……
}

Vm 創(chuàng)建的時候提到過 實例化了一個 HolderFragment 。并且實例化的時候通過上面createHolderFragment 方法將其fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
我們知道commit 之后 fragment 將會擁有靈魂,獲得生命周期。再看其onDestroy方法里
調(diào)用了 mViewModelStore.clear();

 public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }

至此,Google 充分利用了 fragment 生命周期的特性,使得 Vm 完成了 onCleared。
(這里我不得不說一下,Google 的 lifecycle 與 ViewModel 全都是利用 fragment 的一些特性去玩這些生命周期,這么喜歡用 fragment是為什么我現(xiàn)在還沒完全領(lǐng)悟)

那么問題來了 為什么橫豎屏切換 ViewModel 不會 onCleared?
看 HolderFragment 的構(gòu)造方法里有個
setRetainInstance(true);
所以一切都了然了,(如果你沒有了然就百度一下這個方法是干嘛的)
Google 真是充分的利用 fragment 特點。

四、注意

官網(wǎng)用一個大大的紅色感嘆號表明:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
由于 ViewModel 生命周期可能長與 activity 生命周期,所以為了避免內(nèi)存泄漏 Google 禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用。
這個讓我糾結(jié)了好久,后來發(fā)現(xiàn) 有一個 AndroidViewModel 類,內(nèi)部維護了一個 ApplicationContext 實在不行也可以用著個

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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