Jetpack-ViewModel源码分析

ViewModel简介

1. Activity或Fragment有自己的生命周期被Framework所管理,Framework可能会根据用户的一些操作以及设备的状态对Actvity或Fragment进行销毁和重建。伴随着Activity或Fragment的销毁和重建,它们当中的数据也会随着一起销毁和重建。对于一些简单的数据,Activity可以使用onSaveInstanceState()方法,并从onCreate的bundle中重新获取,但这一方法仅仅适合一些简单的UI状态,对于列表型这种庞大的数据类型并不适合。
2. Activity或Fragment经常会做一些异步的耗时操作,需要管理这些异步操作得到的数据,并在destroyed的时候清理它们,从而避免内存溢出这类问题的发生。管理数据的成本较大,另外configurationChanged发生时,部分资源和数据需要重新请求,降低了性能。
3. Activity或Fragment本身需要处理很多用户的输入事件并和操作系统相关操作们,如果还维护管理业务数据和资源,会导致Activity和Fragment过于庞大。

特点

以关联生命周期的方式来存储和管理UI相关的数据,即使configuration发生改变(比如旋转屏幕,系统语言字体变化),数据仍然可以存在不会销毁。
Activity和Fragment之间,多个Fragment之间共享数据,轻松解决Activity和Fragment之间数据通信提高复杂性问题。

职责

ViewModel类图

ViewModel
ViewModel 对象为特定的界面组件提供数据,并包含数据处理业务逻辑,以与模型进行通信。
ViewModel被更换或者清除时,通过onCleard释放持有资源。

ViewModelStore
负责缓存ViewModel,通过clear释放ViewModel持有资源。

ViewModelStoreOwner
负责持有ViewModelStore,Support Library 28中Fragemnt,Activity和HoldFragment是ViewModelStoreOwner的具体实现类,他们内部持有ViewModelStore。

ViewModelStores
负责通过Activity或者Fragment获取ViewModelStore。
1. 如果Activity或者Fragment已实现ViewModelStoreOwner接口,直接返回内部ViewModelStore;
2. 如果Activity或者Fragment未实现ViewModelStoreOwner接口,通过HoldFragment机制获得ViewModelStore;

ViewModelProvider
负责获取ViewModel实例,构造ViewModelProvider需要ViewModelStore和Factory。

1. 根据ViewModel Class名称,从ViewModelStore缓存中获取ViewModel实例;
2. 步骤1获取成功,直接返回实例;
3. 步骤1获取失败,通过Factory反射构造ViewModel,内置Factory有NewInstanceFactory,AndroidViewModelFactory。
4. 使用ViewModelStore缓存步骤3创建的ViewModel实例后,返回ViewModel实例。

ViewModelProviders
负责为Activity和Fragment创建ViewModelProvider。

源码解析

使用

在Activity或者Fragment中获得ViewModel实例。

class MainFragment : Fragment() {
    companion object {
        fun newInstance() = MainFragment()
    }
    private lateinit var viewModel: MainViewModel
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.main_fragment, container, false)
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    }
}

上面的样例代码中使用最简单的方式构造ViewModel,通过ViewModelProvider内部自动构造ViewModel,如果需要把Repository等其他对象内置到ViewModel中,可以自定义Factory去实现。这篇文章主要是分析ViewModel内部实现机制,不对ViewModel的构造太多介绍,可以参见android-architecture-components-BasicSample

关键源码分析

相关源码中比较关键是的ViewModelStores,HoldFragment。

ViewModelStores

public class ViewModelStores {
    private ViewModelStores() {
    }
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }
    public static ViewModelStore of(@NonNull Fragment fragment) {
        if (fragment instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) fragment).getViewModelStore();
        }
        return holderFragmentFor(fragment).getViewModelStore();
    }
}
  1. 如果Activity或者Fragment实现ViewModelOwner,直接返回内部的ViewModelStore;
  2. 否则,通过HoldFragment获得ViewModelStore。

注意:无论哪种方式,都实现了对应Activity或者Fragment只存在一个ViewModelStore,且根据Activity和Fragment生命周期销毁(configurationChanged除外)。

HoldFragment

public class HolderFragment extends Fragment implements ViewModelStoreOwner {
    //仅存在一个HolderFragmentManager实例
    private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
    private ViewModelStore mViewModelStore = new ViewModelStore();
    public HolderFragment() {
//保留Fragment
        setRetainInstance(true);
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sHolderFragmentManager.holderFragmentCreated(this);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }
    
static class HolderFragmentManager {
        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>(); 
private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
...
}

1. HoldFragment实现ViewModelStoreOwner接口;
2.setRetainInstance,configurationChanged后,短暂保留Fragment,从而保留ViewModelStore[参考];
3. onDestory时,ViewModelStore clear;
4. sHolderFragmentManager仅存在一个实例;
5. NotCommitted变量和holderFragmentCreated规避为一个Activity或者Fragment创建多个HoldFragment。

Support Library

support v4 28.0.0 中FragmentActivity和Fragment已实现ViewModelStoreOwner接口,不再需要HoldFragment机制支持ViewModel特性。

Fragment

通过在OnDestroy方法对configuration的判断,保留下ViewModelStore。

public class Fragment implements LifecycleOwner, ViewModelStoreOwner {
    ViewModelStore mViewModelStore;
    public ViewModelStore getViewModelStore() {
        if (this.getContext() == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        } else {
            if (this.mViewModelStore == null) {
                this.mViewModelStore = new ViewModelStore();
            }
            return this.mViewModelStore;
        }
    }
    public void onDestroy() {
        this.mCalled = true;
        FragmentActivity activity = this.getActivity();
        boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
        if (this.mViewModelStore != null && !isChangingConfigurations) {
            this.mViewModelStore.clear();
        }
    }
}

FragmentActivity

了解下NonConfigurationInstances,此处不再赘述。

public class FragmentActivity extends SupportActivity implements ViewModelStoreOwner {
    private ViewModelStore mViewModelStore;
    public ViewModelStore getViewModelStore() {
        if (this.getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
        } else {
            if (this.mViewModelStore == null) {
                FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
                if (nc != null) {
                    this.mViewModelStore = nc.viewModelStore;
                }

                if (this.mViewModelStore == null) {
                    this.mViewModelStore = new ViewModelStore();
                }
            }
            return this.mViewModelStore;
        }
    }
   protected void onCreate(@Nullable Bundle savedInstanceState) {
        this.mFragments.attachHost((Fragment)null);
        super.onCreate(savedInstanceState);
        FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && this.mViewModelStore == null) {
            this.mViewModelStore = nc.viewModelStore;
        }
   }
    protected void onDestroy() {
        super.onDestroy();
        if (this.mViewModelStore != null && !this.isChangingConfigurations()) {
            this.mViewModelStore.clear();
        }
        this.mFragments.dispatchDestroy();
    }
}

总结

Jetpack中ViewModel的设计和实现并不是很复杂,ViewModel是Google官方推荐MVVM模式实现的重要组成部分,ViewModel与Lifecycle和LiveData等Jetpack组件组合使用会更加发挥其作用,待后续深入分析其他Jetpack组件源码实现。

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

推荐阅读更多精彩内容