一点点入坑JetPack:ViewModel篇

前言

费了不少脑细胞,把Lifecycle单拆出来整了一篇文章。那么接下来天然而然的就到了ViewModel,为了让系列像系列的样子,因此这里仍然是单独把ViewModel拿出来。java

你别说单独抽出来,还真有点干干巴巴,麻麻赖赖,一点都不圆润。那还说啥呢?盘它...android

一点点入坑JetPack:ViewModel篇数据库

一点点入坑JetPack:Lifecycle篇服务器

一点点入坑JetPack:LiveData篇网络

一点点入坑JetPack:实战前戏NetworkBoundResource篇app

一点点入坑JetPack(终章):实战MVVM框架

正文

1、ViewModel

新官上任三把火,强敌面前秀三波。对于ViewModel来讲,它算是JetPack框架中堪当中枢的角色,说实话它实在很差单独去聊,更多的是和LiveData共进退。这里必须安利一下,ViewModel+LiveData的确很好用,甚至可能加上Room简直...飘了,拽了,感受本身个头都不矮了;疯了,狂了,敢在宇宙之间称王了....异步

碍于篇幅的缘由,这里单独聊ViewModel,后边会综合介绍展示其强大的战斗力...ide

关于ViewModel来讲,其实仍是蛮简单的。从ViewModel官方的描述来看ViewModel的存在,解决了俩大问题:post

1.一、解决问题1

咱们都知道,当咱们的Activity/Fragment由于某些因素被销毁重建时,咱们的成员变量便失去了意义。所以咱们经常须要经过 onSaveInstanceState()和onCreate()/onSaveInstanceState(Bundle)完成对数据的恢复(一般还要保证其正确的序列化)。而且对于大型数据来书,便有些乏力,好比:List、Bitmap...

而ViewModel就是解决此问题。

1.二、解决问题2

另外一个问题是Activity/Fragment常常须要进行一些异步操做。一旦涉及到异步,咱们都明白这里存在内存泄漏的可能性。所以咱们保证Activity/Fragment在销毁后及时清理异步操做,以免潜在的内存泄漏。

ViewModel并无自动帮咱们解决这个问题,而是经过onCleared()交给咱们业务本身重写去处理。

1.三、使用方法

关于ViewModel的使用,实在没啥好说的。实在是太简单了,一个简单的demo:

class MyViewModel : ViewModel() {
    var name: String = "MDove"
}

// Activity中调用
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
        // TODO model.name
    }
}
复制代码

咱们只须要将想要被保存、被管理的变量,声明在ViewModel的实现类中便可。而后经过ViewModelProviders.of()/get()拿到这个实例。就可能像往常同样自由的使用,而不须要担忧Activity/Fragment重建所带来的一系列问题。

注意警告!

文档在此处,有一个大大的警告:Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

为啥?从上述解决的问题来看,ViewModel很明显生命周期会比Activity要长,所以若是持有Activity相关实例,必然会带来内存泄漏。(那若是的确有业务须要咋整?使用AndroidViewModel(application)便可。)

1.四、Fragment共享

值得注意的一点:of方法须要传递一个Activity/Fragment。由于ViewModel须要与其生命周期绑定。既然能够传递一个Activity,那么咱们就可以猜到:是否是对于此Activity下的Fragment这个ViewModel也是可见的?

没错,正是如此。官方也做出了解读:Activity中的两个或多个Framgent须要相互通讯是很常见的,这个常见的痛点能够经过使用ViewModel对象来解决,这些Fragment能够共享ViewModel来处理通讯问题。

因此咱们在同Activity下,不一样的Fragment实例,能够直接经过传入activity,拿到一样的ViewModel实例,进而实现数据通信。

真的很方便...

2、源码

若是咱们打开ViewModel的源码,咱们会发现...

public abstract class ViewModel {
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}
复制代码

就是一个抽象类,没错,整个ViewModel的设计就是很简洁,咱们往ViewModelProviders中继续看:

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}
复制代码

咱们能够看到在实例化ViewModelProvider中,须要传一个ViewModelStore,而这个ViewModelStore直接经过传入的FragmentActivity中拿,让咱们走进去看一看:

@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}
复制代码

咱们能够看到,这个ViewModelStore是在FragmentActivity中是一个mViewModelStore的变量。这个ViewModelStore是什么?从名字能够看出它是一个ViewModel的Store。

ViewModelStore的很简单,就是一个Map在后文中会展开。

最开始我看到这时,很懵。ViewModel是保证咱们重建后实例的惟一,但是这居然是一个成员变量,很明显重建后变量就没了?!...(PS:固然有这种疑问,是由于我本身蠢...)

怎么肥死,小老弟??...其实这里是没问题的,咱们仔细看一看,这个mViewModelStore赋值是经过这一行代码:

NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
    mViewModelStore = nc.viewModelStore;
}
复制代码

没错,就是这行代码,保证了咱们重建后恢复原来的mViewModelStore,进而保证了咱们的ViewModel的惟一性。

@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);
    return (T) viewModel;
}
复制代码

mViewModelStore源码 -> ViewModelStore源码,很常见的Map存储操做

// 
public class ViewModelStore {

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

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

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

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

3、小总结

从ViewModel的使用上来讲,彷佛并无什么“船新的版本”...更多的是帮咱们搞定了一些现存的坑。的确是如此,但其实ViewModel更可能是带来了一种思想:数据驱动,也就是MVVM。

ViewModel做为中枢,担任了从数据源拿数据,交由LiveData通知UI层更新UI。用一张图来解释这种变革:

Google Sample为Repository的编写,提供了一个很巧妙的设计:NetworkBoundResource。全类一共有120+的代码,却基于LiveData+ViewModel帮咱们约束了:从服务器取从数据库取网络获取失败,从数据库取...等等一系列网络请求、本地请求约数。

关于这个类的设计与用法,会在后续的实战篇一点点展开。没错,当你用上它们,你会爱上这款“游戏”。

尾声

今天的文章想聊的内容就到此结束了,更多的是ViewModel的一个引子。毕竟对于咱们来讲,我tm不须要知道这些,只须要告诉我怎么写就行。老夫写代码就是ctrl+c/v!

不着急,一点点来。后边我会把业务中正在运行的代码拿出来,作实战操做分析。饭要一口口的吃,文章要一篇篇的写...

我是一个应届生,最近和朋友们维护了一个公众号,内容是咱们在从应届生过渡到开发这一路所踩过的坑,以及咱们一步步学习的记录,若是感兴趣的朋友能够关注一下,一同加油~

我的公众号:咸鱼正翻身
相关文章
相关标签/搜索