ViewModel源码分析

一、ViewModel的简介

2018年谷歌IO大会上正式发布了JetPack组件,其中包括Databing、Lifecycles、LiveData、Navigation、Paging、Room、ViewModel、WorkManager等一系列框架。并且发布androidx包,这些框架的最新版本组件将引入到androidx包下。
JetPack架构组件的发布,意味着google终于拿出了官方推荐的Android开发架构,一直以来在Android开发上老生常谈三种开发模式,MVC、MVP、MVVM。整体的思想就是做一些代码封装和操作上的分离,具体三种架构的区别不在这详细描述。说一点儿个人对这三中开发模式的理解。
MVC:Activity承担C的任务,强行carry大量业务和数据代码。
MVP:Presenter承担主要代码任务,连接数据层和视图层的交互。
MVVM:Model负责数据、View负责展示、ViewModel负责Model和View的交互,同时,最好的一点是View层自动监听ViewModel的数据变化。
在JetPack组件出现之前,一直以来构建MVVM架构的方式都是代用Databing来充当ViewModel,而Databing是采用标签方式写入布局文件中的,这样一来当出现问题的时候不是太好进行调试。所以ViewMode的出现对于MVVM模式开发具有重大的意义。

二、ViewModel的用法和特点
ViewModel的使用是要结合LiveData框架进行的,LiveData框架也是JetPack组件的一部分,这里先不详细进行介绍。ViewModel有两个特点,一是更加方便的保存数据,第二个特点,也是最重要的特点,就是保证数据不受Activity的销毁重建所影响,当Activity销毁重建后仍然能收到之前的数据。

1)继承ViewModel

 class MainViewModel : ViewModel() {
    private val repertory: MainRepository by lazy { MainRepository() }
    var data: MutableLiveData<JsonBean> = MutableLiveData()
    fun getDataFromServer(){
    repertory.getDataFromServer(data)
     }
    }

ViewModel中持有LiveData,LiveData是ViewModel持有数据的载体。

2)Activity通过ViewModelProviders获取ViewModel

class MainActivity : AppCompatActivity() {
private lateinit var mModel: MainViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    initData()
}

private fun initData() {
    mModel = ViewModelProviders.of(this)[MainViewModel::class.java]
    mModel.data?.observe(this, Observer {
        val mainAdapter = MainAdapter(this, it)
        val linearLayoutManager = LinearLayoutManager(this)
        rv.layoutManager = linearLayoutManager
        rv.adapter = mainAdapter
    })
    mModel.getDataFromServer()
 }  
}

3)通过ViewModel中的方法获取数据然后改变LiveData状态,通过响应式的方式通知到Activity进行视图更新。用法上比较简单。

三、源码分析

个人认为对于一个框架的学习仅仅掌握框架的用法是不够的,因为这太简单了,曾经看到过一篇博客,里面有一句话说的很好,”如果你只会用这个框架,而不清楚其中的原理和设计,那么即便这个框架性能再好,再牛逼,那也是写框架的那个人牛逼,而不是你牛逼“。我认为这句话说的很好,所以当我们学习一中新框架的时候,不能单单只会用,一定要清楚其中的原理,尽量去理解作者的设计思想,就像品茶一样,这样才能品出里面的滋味。不扯闲篇了,由于作者能力也有限,所以以上和以下分析,如有不同观点或作者描述有误请给予批评指正,欢迎来喷。

1)先从获取ViewModel的类入手,”ViewModelProviders“从类名中也能分析出这个类是用于提供ViewModel的。源码如下:

public class ViewModelProviders {
public ViewModelProviders() {
}

private static Application checkApplication(Activity activity) {
    Application application = activity.getApplication();
    if (application == null) {
        throw new IllegalStateException("Your activity/fragment is not yet attached to "
                + "Application. You can't request ViewModel before onCreate call.");
    }
    return application;
}

private static Activity checkActivity(Fragment fragment) {
    Activity activity = fragment.getActivity();
    if (activity == null) {
        throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
    }
    return activity;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
    ViewModelProvider.AndroidViewModelFactory factory =
            ViewModelProvider.AndroidViewModelFactory.getInstance(
                    checkApplication(checkActivity(fragment)));
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    ViewModelProvider.AndroidViewModelFactory factory =
            ViewModelProvider.AndroidViewModelFactory.getInstance(
                    checkApplication(activity));
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
    checkApplication(checkActivity(fragment));
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @NonNull Factory factory) {
    checkApplication(activity);
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@SuppressWarnings("WeakerAccess")
@Deprecated
public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory} or
     * {@link ViewModelProvider.AndroidViewModelFactory#getInstance(Application)}.
     */
    @Deprecated
    public DefaultFactory(@NonNull Application application) {
        super(application);
    }
}
}

我们从中可以看到,这个类中的方法全是静态的,说明这个类相当于一个工具类,从of方法中我们可以看到真正实现ViewModel创建的类是ViewModelProvider,而这个类的构建又需要通过ViewModelStores的of方法获取,ViewModelStores的代码如下:

public class ViewModelStores {
private ViewModelStores() {
}

@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
    if (activity instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) activity).getViewModelStore();
    }
    return holderFragmentFor(activity).getViewModelStore();
}

@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
    if (fragment instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) fragment).getViewModelStore();
    }
    return holderFragmentFor(fragment).getViewModelStore();
}
}

从这个类中可以发现获取ViewModelStore是通过构建一个HoldFragment,而这个HoldFragment的作用就是持有ViewModelStore的引用,然后将这个Fragment添加到Activity中,而且这个Fragmet有一个重要的特点,在实例化的时候会调用setRetainInstance(true)方法,这个方法的作用是让Fragment不受Activity销毁重建影响,这样一来就能保证ViewModel不会由于Activity的销毁重建导致数据丢失,这是ViewModel的一个重要特性。这种设计确实很巧妙,HolderFragment的代码如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
.......省略部分代码

public HolderFragment() {
    setRetainInstance(true);
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
    super.onDestroy();
    mViewModelStore.clear();
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
    return mViewModelStore;
}
/**
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
    return sHolderFragmentManager.holderFragmentFor(activity);
}

/**
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
    return sHolderFragmentManager.holderFragmentFor(fragment);
}
.....省略部分代码
    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;
    }

    HolderFragment holderFragmentFor(Fragment parentFragment) {
        FragmentManager fm = parentFragment.getChildFragmentManager();
        HolderFragment holder = findHolderFragment(fm);
        if (holder != null) {
            return holder;
        }
        holder = mNotCommittedFragmentHolders.get(parentFragment);
        if (holder != null) {
            return holder;
        }

        parentFragment.getFragmentManager()
                .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
        holder = createHolderFragment(fm);
        mNotCommittedFragmentHolders.put(parentFragment, holder);
        return holder;
    }
}

之后来到ViewModelProvider,代码量不是很多,但是里面的设计确实很有意思。首先来分析它的成员变量,成员变量只有两个,Factory从字面的意思就能理解,它是生产ViewModel的工厂,所以这里用到了工厂模式。ViewModelStore它用来存储ViewModel,相当于ViewModel的缓存。内部是一个HashMap,key是ViewModel的类名称。ViewModelProvider代码如下:

public class ViewModelProvider {
....省略部分代码
private final Factory mFactory;
private final ViewModelStore mViewModelStore;

......
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore = store;
}

public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
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;
    .......省略部分代码
}

2)Factory是ViewModelProvider中的一个接口,定义如下:

public interface Factory {
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

通过create方法创建ViewModel。Factory有两个实现类AndroidViewModelFactory、NewInstanceFactory都是ViewModelProvider中的静态内部类。同时AndroidViewModelFactory又继承于NewInstanceFactory。有一种依赖注入的思想在里面。接下来我们来分析两个Factory的实现类。NewInstanceFactory最终通过newinstance方法创建ViewModel实例。

public static class NewInstanceFactory implements Factory {
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }
    private Application mApplication;

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

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

最后,真正存储ViewModel的容器是ViewModelStore,ViewModelStore中维护了一个HashMap,代码如下:

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

}

文章代码库:https://github.com/24KWYL/MVVM.git

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,635评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,543评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,083评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,640评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,640评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,262评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,833评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,736评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,280评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,369评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,503评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,870评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,340评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,460评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,909评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,512评论 2 359

推荐阅读更多精彩内容