Android(安卓)JetPack学习笔记之ViewModel
Android JetPack学习笔记之ViewModel
- 前言
- ViewModel
- LiveData(简要说明)
- ViewModel源码分析
- ViewModel的使用
前言
Jetpack 是 Android 软件组件的集合,使您可以更轻松地开发出色的 Android 应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。
最近有空边学习边把学习笔记做了。巩固知识,输入输出,强化学习,如有不对望指出,感谢。
(以下是对官方代码文档的翻译和自己的部分见解)
如果引用JetPack库
implementation 'android.arch.lifecycle:extensions:1.1.1'
ViewModel
ViewModel是一个负责用于准备和管理Activity或者Fragment的数据的类,它也负责对Application中其他的Activity和Fragment进行数据通信(比如:调用业务逻辑类,Fragment之间的数据同步,Activity和Fragment的数据同步。后面的列子会讲到)
- ViewModel也可以在Activity/Fragment的作用域中创建,生命周期跟随作用域,跟随着Activity的finish销毁而销毁。
- 换句话说,这也意味着,如果ViewModel的持有者(Activity/Fragment)因为配置的改变(例如:rotation)而销毁但是ViewModel没有被销毁,那么当持有者(Activity/Fragment)重新生成实例后将重新与已存在的这个ViewModel重新绑定。
- ViewModel的目的是用于为一个Activity/Fragment 获取和保存必要的信息,Activity/Fragment 应该能够观察ViewModel内容的变化。ViewModel通常和LiveData或者Android Data Binding来暴露这些信息。您还可以使用您喜欢的框架中的任何Observer类。
- ViewModel只负责管理UI的数据,它不应该直接访问你的视图层次和持有你的Activity/Fragment的引用。(例如:不把视图对象或者Activity/Fragment的Context作为对象传入到ViewModel中。避免内存泄漏)
LiveData(简要说明)
LiveData是一个用于保管数据的类。
ViewModel源码分析
启动 Android Studio 3.2 或以上版本,就能使用具有ViewModel的Fragment和Activity。
生成目录结构如下
系统自动生成了MainActivity,MainFragment,MainViewModel。
PS:MainViewModel继承于抽象类ViewModel,ViewModel中有个onCleared的方法,这个方法适用于ViewModel长时间没有被使用,这对于当ViewModel观察一些数据并且你需要清理ViewModel的订阅以防止泄漏很有用。
ViewModel的产生过程如图:
Created with Raphaël 2.2.0 ViewModelProviders.of() 获得ViewModelProvider对象调用get方法 ViewModelProvider.Factory创建ViewModel ViewModelStore是否有ViewModel 返回ViewModel实例 yes no以下是生成源码分析
//MainFragment中将生成如下代码@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mViewModel = ViewModelProviders.of(getActivity()).get(MainViewModel.class); // TODO: Use the ViewModel //我在这里创建了个LiveData并 //添加了一个Observer用于观察User数据的变化, //并为TextView赋值 mViewModel.getUserLiveData().observe(this, new Observer<User>() { @Override public void onChanged(@Nullable User user) { message.setText(user.name); } }); }
ViewModel是通过ViewModelProviders.get方法获得的。而get方法是用于返回一个与当前Activity/Fragment绑定的已存在ViewModel或者创建一个新的绑定的ViewModel。那这里是如何判断是否已存在ViewModel或者创建一个新的ViewModel呢?源码如下:
//1.ViewModelProviders.of()方法获得ViewModelProvider对象,//ViewModelProvider方法调用get如下@NonNull @MainThread public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { //2.返回当前类的更具体的名字 String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } //3.把这个canonicalName作为key获取 return get(DEFAULT_KEY + ":" + canonicalName, modelClass); }@NonNull @MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { //4.通过key获得之前已存在的ViewModel对象 ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { //noinspection unchecked return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } //5.如果ViewModel为空则通过ViewModelProvider.Factory创建一个新的ViewModel // viewModel = mFactory.create(modelClass); //6.并把viewModel存入ViewModelStore中. mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }//7.ViewModelProvider.Factory如何创建的ViewModel?//AndroidViewModelFactory是ViewModelProvider的静态内部类public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {//部分代码已省略@NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { //8.通过反射机制创建实例/如果失败调用默认构造器创建实例 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); }}//9.ViewModel的存储类ViewModelStorepublic class ViewModelStore {//10.整个Application的ViewModel实例都被存储于Map中。 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();//清除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(); }}
ViewModel的使用
一个简单的例子。Activity中的一个按钮,点击以后,Fragment内部的文字实现变化。
在JetPack出现之前。我们大概会使用如下方法:Fragment暴露个方法给Activity中调用,EventBus,BoradCastRecevie等。但这都有缺陷,当然还有RxBus.现在有了JetPack,谷歌统一框架后,我们就能优雅的在Fragment和Activity之间共享数据了。
示例代码如下:
MainActivity.class
public class MainActivity extends AppCompatActivity { private MainViewModel mainViewModel; private Button send; int count=1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .replace(R.id.container, MainFragment.newInstance()) .commitNow(); } send=findViewById(R.id.send); send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { count++; mainViewModel.post("1001","Test"+count); } }); }}
MainFragment
public class MainFragment extends Fragment { private MainViewModel mViewModel; private TextView message; public static MainFragment newInstance() {return new MainFragment();} @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.main_fragment, container, false); message=view.findViewById(R.id.message); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mViewModel = ViewModelProviders.of(getActivity()).get(MainViewModel.class); // TODO: Use the ViewModel mViewModel.getUserLiveData().observe(this, new Observer<User>() { @Override public void onChanged(@Nullable User user) { message.setText(user.name); } });}}
MainViewModel
public class MainViewModel extends ViewModel { private MutableLiveData<User> userLiveData; public LiveData<User> getUserLiveData() { if(userLiveData==null){ userLiveData=new MutableLiveData<>(); } return userLiveData; } public void post(String id, String name){ userLiveData.setValue(new User(id,name)); }}
代码很优雅,逻辑很清晰。
这里没有直接使用LiveData而是MutableLiveData。JetPack中不允许我们直接操作LiveData而是使用MutableLiveData(可变数据)。
public class MutableLiveData<T> extends LiveData<T> { @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); }}
MutableLiveData中postValue和setValue有什么区别呢?
其实源码中的注释描述的很清楚。
postValue是创建一个任务在主线程中用于设置你给的value。
而setValue是直接在主线程执行该方法。
liveData.postValue("a"); liveData.setValue("b"); The value "b" would be set at first and later the main thread would override it with the value "a".
官方给的例子很明显,同时调用后,“a"参数将覆盖"b”。
下次我们仔细来看看LiveData的源码吧。
更多相关文章
- 关于Android自定义相机进行拍照(小米手机出现异常的原因)
- 对“Android输入事件流程中的EventHub分析及源码演示”的补充
- [置顶] android中自定义View
- Android创建和使用数据库SQLIte
- Android实现下拉框(Spinner)
- Android触摸事件传递
- [置顶] Android存储选择
- Android(安卓)Service完全解析
- Android(安卓)ICON生成及优化