谷歌架构组件(一)ViewModel的使用与分析

ViewModel 是google推出的架构组件之一,它被设计用于存储和管理UI相关的数据。

背景:

1方便数据存储

以生命周期的方式存储和管理UI相关数据。当屏幕旋转等改变时,数据能够被恢复。

2生命周期控制

使用fragment时我们都会对它复杂的生命周期处理感到痛苦,稍有不慎报各种null异常。ViewModel,Lifecycle等组件的推出,可以有效的解决生命周期的处理问题,提高app的稳定性。`

3 DataBinding数据驱动UI

从字面意思看ViewModel就是activity fragment的数据抽象模型。我们知道activity fragment是由系统管理,不受app控制,当内存不足时,系统随时都有可能回收杀死app。所以这也是android app开发具有挑战的地方,我们需要在不确定的环境下保证我们的业务流程顺利进行。由于ViewModel设计是和activity fragment的生命周期是解耦的,所以当activity fragmegnt重新create时,如果ViewModel已经创建过,则仍使用原ViewModel。

ViewMode生命周期见下图:


viewmodel-lifecycle.png

如何使用:

1自定义ViewModel

    class UserModel extends ViewModel{  
      String name;  
      String age;  
      
    } 

2 获取ViewModel

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.  
      
            UserModel model = ViewModelProviders.of(this).get(UserModel.class);  
            
        }  
    } 

因为ViewModel生命周期感知的,所以我们不需要手动释放,使用起来非常方便。

注意事项:

ViewModel不可以持有activity fragment等view的引用,否则会导致内存泄漏。

读到这里你可能会有如下疑问:

  1. ViewModel 为什么不可以持有activity fragment等View的引用

  2. ViewModel 如果感知activity fragment的生命周期

  3. ViewModel 如何保存数据

接下来我们根据上述疑问,跟踪ViewModel 的相关代码具体分析。

111.png

以ViewModelProviders.of(this).get(UserModel.class); 这行代码作为切入点

@MainThread  
public static ViewModelProvider of(@NonNull Fragment fragment) {  
    FragmentActivity activity = fragment.getActivity();  
    if (activity == null) {  
        throw new IllegalArgumentException(  
                "Can't create ViewModelProvider for detached fragment");  
    }  
    initializeFactoryIfNeeded(activity.getApplication());  
    return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory);  
} 

1 创建默认的工厂方法,实例化新的ViewModels

2 通过传入fragment或者acitity参数,new ViewModelProvider实例并返回。

这里有2个关键的代码点

1 ViewModelStores.of(fragment)

2 return new ViewModelProvider(…)

针对代码点1 ViewModelStores.of(fragment) 查看ViewModelStores源码

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

它通过holderFragmentFor函数返回ViewModelStore对象

继续跟踪holderFragmentFor函数,找到HolerFragment 这个关键类

从HolerFragment类的代码中我们找到了ViewModel感知fragment,activity生命周期的原因。

Google在底层默默的create了一个新的HolderFragment 对象,

由HolderFragment负责生命周期感知,当onDestroy()时清理ViewModelStore

持有ViewModelStore对象,setRetainInstance(true) 保证当界面旋转被销毁再重建时保证mViewModelStore 不被销毁。但对于因内存被系统杀死后重新进入,数据不会被恢复。

public class HolderFragment extends Fragment {    
  
      private ViewModelStore mViewModelStore = new ViewModelStore();  
  
      public HolderFragment() {  
        setRetainInstance(true);  
      }  
      
        HolderFragment holderFragmentFor(Fragment parentFragment) {    
                FragmentManager fm = parentFragment.getChildFragmentManager();    
                HolderFragment holder = findHolderFragment(fm);    
                ......
                holder = createHolderFragment(fm);    
                mNotCommittedFragmentHolders.put(parentFragment, holder);    
                return holder;    
            }    
        
    } 

解决了ViewModel如果感知生命周期的问题,我们再分析下ViewModel的数据存储。

我们查看ViewModelStore这个类,发现ViewModel是被存储在一个HashMap内,所以它是保存在app内存中,并没有做持久化处理。

public class ViewModelStore {  
  
    private final HashMap<String, ViewModel> mMap = new HashMap<>();  
  
    ......

} 

针对代码点2 return new ViewModelProvider(…),我们看下ViewModelProvider。

从代码中可知ViewModelProvider是对ViewModelStore 和Factory的封装,当viewModel存着时获取ViewModel,如果不存在则调用工厂方法创建ViewModel。

public class ViewModelProvider {  
    private final Factory mFactory;  
    private final ViewModelStore mViewModelStore;  
  
    public ViewModelProvider(ViewModelStore store, Factory factory) {  
        mFactory = factory;  
        this.mViewModelStore = store;  
    }  
  
      
        @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 {  
                ......  
            }  
      
            viewModel = mFactory.create(modelClass);  
            mViewModelStore.put(key, viewModel);  
            //noinspection unchecked  
            return (T) viewModel;  
        }  
      
    } 

总结:

通过代码的跟踪分析我们解决了3大疑问,发现ViewModel的实现主要依赖于HolderFragment类的实现。

  • 通过添加新的HolderFragment感知生命周期的变化

  • 通过HolderFragment持有ViewModelStore对象

  • ViewModel存储在ViewModelStore的hashmap内存中,不做持久化数据存储,当activity fragment处于后台因内存问题被系统杀死后,重新进入后数据不会被恢复。

  • Fragment和Activity作为key访问获取ViewModel对象

  • ViewModel不能持有activity,fragment等view的引用,避免内存泄漏

参考
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
android source code 8.0

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

推荐阅读更多精彩内容