Android Jetpack架构组件之 ViewModel (源码篇)

2,424 阅读7分钟

1、前言

最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面。

Android Architecture组件是Android Jetpack的一部分,它们是一组库,旨在帮助开发者设计健壮、可测试和可维护的应用程序,包含一下组件:

上述时Android Architecture所提供的架构组件,本文主要从使用和源码的角度了解一下ViewModel组件的机制

2、VIewModel简介

Android框架管理UI控制器的生命周期,例如活动和片段。框架可以决定销毁或重新创建UI控制器以响应完全不受您控制的某些用户动作或设备事件,那设想一种情况当用户在界面操作录入了一些信息后,因为某种原因导致Activity重新创建,那此时用户写好的信息呢?如果要重头再来可能有的用户就会不耐烦了,进而减少了使用。。。,可能有人说,可以使用该 onSaveInstanceState()方法并从包中恢复其数据 onCreate(),但此方法仅适用于可以序列化然后反序列化的少量数据,如果要恢复的数据量比较大,此时就时VIewModel的厉害之处了。

ViewModel之所以能在Activity重建时保存并恢复数据,因为Activity初次创建时会初始化创建VIewModel,在Activity销毁时,ViewModel对象不会销毁,在新的Activity重新创建后,仍然会执行之前的获取ViewModel的过程,Android系统采取了处理机制,使现在拿到的ViewModel就是前一次创建的对象,设想一下数据都储存在VIewModel中,而两次拿到的都是同一个VIewModel,那显示的数据自然就和之前的一样喽,这里先放一张系统处理ViewModel的创建、存储和获取流程图:

3、ViewModel的使用

  • 创建ViewModel类继承系统的ViewModel
class Model : ViewModel() {
    var textName = "Empty"
}
  • 在使用的Activity、Fragment中获取ViewModel
     val model = ViewModelProviders.of(this)[Model::class.java]
  • 设置ViewModel中储存的数据
 tvModel.text = model.textName
  • 实例:先添加一个按钮,点击按钮会修改Model中textName的值,然后在旋转手机,如果显示的为第二次设置的值,那么说明数据被保存且恢复了;
btn_change.setOnClickListener {
            model.textName = "Change = 22222"
            tvModel.text = model.textName
        }

  • 注意:因为ViewModel在Activity销毁时是不会重新创建的,这也意味者ViewModel中不可以引用Activity的对象,否则会有内存泄露的问题,那么当Model中需要Context呢?Android为我们提供了AndroidViewModel,只需继承AndroidViewModel即可

4、生命周期

  • ViewModel对象的范围是在获取ViewModel时传递给ViewModelProvider的Lifecycle生命周期
  • ViewModel在内存中直到Activity销毁或Fragment被移除
  • 系统首次调用活动对象的onCreate()方法时,通常会请求ViewModel
  • 系统可能会在整个活动的整个生命周期中多次调用onCreate(),例如当设备屏幕旋转时
  • ViewModel从第一次请求ViewModel直到活动完成并销毁时存在


5、源码解析

5.1 、ViewModelProviders.of(this)[Model::class.java]

从上面的方法中可以看出ViewModel的获取过程分为两步:

  • 获取ViewProvider:

ViewModelProviders提供四个构造方法创建VIewProvider,两个带有factory两个没有,不过没有factory的其实使用的是默认的Factory,所以四个方法基本一致只是Fragment和Activity的区分

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }

   
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

@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(ViewModelStores.of(activity), factory);
    }
  • 获取ViewModelStore:由前面的源码可以知道创建ViewProvider时传入两个参数:ViewModelStore 和 Factory;显然从名字就可以看出他们的作用,Factory负责创建,ViewModelStore负责存储
  @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;
    }

上面的执行是,先去ViewModelStore中获取,如果为空就调用Factory的create()创建ViewModel,并储存在VIewmoStore中,与我们所想一致;

  • ViewModelProviders.of(this)[Model::class.java]方法,忽略创建和储存细节,执行逻辑为:

5.2 、VIewModelStore

  • ViewModelStore.of(this)

上述过程中使用ViewModelStore.of(this) 创建ViewModelStore,方法源码:

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

先判断Activity是否为 ViewModelStoreOwner,如果是直接获取其中的ViewModelStore,否则调用holderFragmentFor(activity).getViewModelStore()获取

  • holderFragmentFor(activity).
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

// HolderFragmentManger

 HolderFragment holderFragmentFor(FragmentActivity activity) {
            FragmentManager fm = activity.getSupportFragmentManager();
            HolderFragment holder = findHolderFragment(fm); // 根据TAG查找Fragment
            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);  // 创建HolderFragment
            mNotCommittedActivityHolders.put(activity, holder); // 保存到集合
            return holder;
        }
  • findHolderFragment() & createHolderFragment()
private static HolderFragment findHolderFragment(FragmentManager manager) {
            if (manager.isDestroyed()) {
                throw new IllegalStateException("Can't access ViewModels from onDestroy");
            }
             // 根据TAG查找
            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
            ...
            return (HolderFragment) fragmentByTag;
        }


 private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
            HolderFragment holder = new HolderFragment();
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();   // 设置TAG
            return holder;
        }

5.3、HolderFragment

上面的过程都是获取或创建HolderFragment的过程,有没有想过我们要的是储存ViewModel的地方,为什么一值在操作Fragment,答案就在其中:

  • 首先记得前面的判断吗?instanceof ViewModelStoreOwner?相信此时应该会想到HolderFragment就是ViewModelStoreOwner的实现类了吧
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
}
  • HolderFragment中保存了ViewStore的实例,
    private ViewModelStore mViewModelStore = new ViewModelStore();

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }
  • 获取ViewStore的执行逻辑:

  • ViewStore是如何获取、储存Viewmodel的呢?
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);
    }

整个ViewModel的源码分析到此结束了,总结一下内部的储存逻辑

  1. 根据传入的Activity获取、创建、添加并已键值对保存Fragment
  2. 获取Fragment中保存的ViewStore对象(ViewStore中使用Map储存ViewModel)
  3. 创建ViewProvider实例,ViewProvider中封装了获取的ViewStore和创建用的Factory
  4. 从VIewStore的Map中或Factory的create()中获取ViewModel

6、思考

  • 如何保证两次创建的activity(旋转前后)获取到的为同一个ViewModel,

在上面获取Fragment中时,创建过HolderFragment后保存在Map中,现在我们看一下这个map

 private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();

Map的键就是Activity的实例,所以无论多少次创建都是此Activity的实例,也就获得唯一的一个对应的Fragment

  • 针对上面的特性是否有其他用处

按照上一个问题的逻辑,主要是传入的Activity的对象一致,那获取到就是同一个Fragment,存储的也是同一个VIewStore,那设想一下,如果一个Activity中有多个Fragment,利用这个特性就可以实现数据交互了;例如:两个Fragment之间。一个显示标题列表,点击某一个标题,另一个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.
        });
    }
}
  1. 上述代码中使用了LiveData,暂且把他看成一个观察改变并发射数据的功能,在第一个Fragment中修改,第二个Fragment也会响应变化;
  2. 在获取ViewModelProvider时,这两个片段都使用getActivity() 因此,这两个片段都会收到相同的SharedViewModel实例,该实例的作用域为Acyivity
  3. 使用ViewModel共享数据的好处,两个Fragment之间,Fragment和Activity之间无需任何联系,实现真正的解耦,每个片段都有其自己的生命周期,并且不受其他生命周期的影响

到此ViewModel的介绍完成了,将ViewModel和LiveData或其他组件联合使用,构造数据驱动型的界面,相信一定会带来不一样的体验。