Lifecycle LiveData LiveData LiveData
0001Lifecycle
原文:http://shymanzhu.com/2017/12/02/Android%20架构组件%EF%BC%88一%EF%BC%89——Lifecycle-Aware%20Components/#more
有一天“谷歌开发者”官微推送了 《正式发布 Android 架构组件 1.0 稳定版 | 附带中文介绍视频》,发现这种架构足够秒杀MVP、MVVM,虽然之前的Google I/O大会中也介绍过,但是这次推出是稳定版,而且是可以投入到生产中去。于是就顺着这篇去官网看了使用文档—— 《Guide to App Architecture》。为了能够更好的理解架构组件的原理,准备先从Lifecycle入手,一步步去理解。
为什么需要Lifecycle组件
Lifecycle组件包括LifecycleOwner、LifecycleObserver。为什么需要Lifecycle组件?一项新的技术的提出肯定是为了解决痛点问题,如果使用过MVP模式的话,有个问题:Presenter感知Activity或者Fragment的生命周期?你可能会这样做,Presenter中定义多个和Activity或者Fragment相应的生命周期方法,然后在Activity或者Fragment中调用Presenter中定义的方法。比如下面的这个例子:
public class MainPresenter implements IPresenter { public MainPresenter(Context context){ } @Override public void onCreate() { } @Override public void onStart() { } @Override public void onResume() { } @Override public void onPause() { } @Override public void onStop() { } @Override public void onDestroy() { }}
public interface IPresenter { void onCreate(); void onStart(); void onResume(); void onPause(); void onStop(); void onDestroy();}
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private IPresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: "); setContentView(R.layout.activity_main); mPresenter = new MainPresenter(this); mPresenter.onCreate(); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart: "); mPresenter.onStart(); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume: "); mPresenter.onResume(); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause: "); mPresenter.onPause(); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop: "); mPresenter.onStop(); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: "); mPresenter.onDestroy(); }}
上面的例子很容易理解,IPresenter中定义和生命周期相关的几个方法,然后在MainActivity中调用对应的方法。这样做会有个问题:在MainActivity中的每个生命周期方法中都要调用一次IPresenter中的接口,对于爱偷懒的程序员来说,这是不能接受的,所以Lifecycle组件就诞生了。
public class MainPresenter implements IPresenter { private static final String TAG = "MainPresenter"; public MainPresenter(Context context){ } @Override public void onCreate(LifecycleOwner owner) { Log.d(TAG, "onCreate: "); } @Override public void onStart(LifecycleOwner owner) { Log.d(TAG, "onStart: "); } @Override public void onResume(LifecycleOwner owner) { Log.d(TAG, "onResume: "); } @Override public void onPause(LifecycleOwner owner) { Log.d(TAG, "onPause: "); } @Override public void onStop(LifecycleOwner owner) { Log.d(TAG, "onStop: "); } @Override public void onDestroy(LifecycleOwner owner) { Log.d(TAG, "onDestroy: "); }}
public interface IPresenter extends DefaultLifecycleObserver{}
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private IPresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: "); setContentView(R.layout.activity_main); mPresenter = new MainPresenter(this); getLifecycle().addObserver(mPresenter); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart: "); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume: "); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause: "); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop: "); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: "); }}
代码也很简单,IPrestener接口继承DefaultLifecycleObserver接口,然后MainPresenter实现IPresenter接口,在MainPresenter中,我把DefaultLifecycleObserver中生命周期的方法全实现了,也可以选择实现其中几个,比如你只关心MainActivity的onCreate() 和onDestroy(),那么你就可以在MainPresenter中实现onCreate()和onDestroy()方法。借助实现DefaultLifecycleObserver接口可以让我们少写很多代码。知道为什么需要Lifecycle组件后,你可能会问了,Lifecycle组件实现的原理是什么?这就是下面我要将的内容了。
Lifecycle组件原理
一般讲到原理,你可能会看到一堆文字的描述,然后就没有看下去的欲望了,能不能来点生动的,比如带点颜色的?好,我懂你,来张高清无码图。
我们以V4包中的Fragment(AppCompatActivity类似)为例,看下Fragment和LifecycleOwner、LifecycleObserver、Lifecycle之间的类关系图。
Lifecycle组件成员Lifecycle被定义成了抽象类,LifecycleOwner、LifecycleObserver被定义成了接口;
Fragment实现了LifecycleOwner接口,该只有一个返回Lifecycle对象的方法getLifecyle();
Fragment中getLifecycle()方法返回的是继承了抽象类Lifecycle的LifecycleRegistry。
LifecycleRegistry中定义嵌套类ObserverWithState,该类持有GenericLifecycleObserver对象,而GenericLifecycleObserver是继承了LifecycleObserver的接口。
对于Fragment、Lifecycle、LifecycleOwner、LifecycleObserver的关系有了一定了解后,可以看一个更直观的图,也就是下面的时序图:
我们在Fragment(AppCompatActivity也一样)中调用getLifecycle()方法得到LifecycleRegistry对象,然后调用addObserver()方法并将实现了LifecycleObserver接口的对象作为参数传进去。这样一个过程就完成了注册监听的过程。
后续就是Fragment生命周期变化时,通知LifecycleObserver的过程:Fragment的performXXX()、onXXX()方法;LifecycleRegistry的handleLifecycleEvent()方法;LifecycleObserver的onXXX()方法。
如果你细心点看上面的时序图,你会发现Fragment中performCreate()、performStart()、performResume()会先调用自身的onXXX()方法,然后再调用LifecycleRegistry的handleLifecycleEvent()方法;而在performPause()、performStop()、performDestroy()中会先LifecycleRegistry的handleLifecycleEvent()方法 ,然后调用自身的onXXX()方法。比如上面的例子中,打印出来的结果也确实符合我们的分析,打印结果如下:
12-01 14:12:59.250 1398-1398/com.shymanzhu.architecture D/MainActivity: onCreate: 12-01 14:12:59.342 1398-1398/com.shymanzhu.architecture D/MainPresenter: onCreate: 12-01 14:12:59.345 1398-1398/com.shymanzhu.architecture D/MainActivity: onStart: 12-01 14:12:59.345 1398-1398/com.shymanzhu.architecture D/MainPresenter: onStart: 12-01 14:12:59.346 1398-1398/com.shymanzhu.architecture D/MainActivity: onResume: 12-01 14:12:59.346 1398-1398/com.shymanzhu.architecture D/MainPresenter: onResume: 12-01 14:12:59.601 1398-1412/com.shymanzhu.architecture D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true12-01 14:12:59.609 1398-1398/com.shymanzhu.architecture I/imx6.gralloc: open gpu gralloc module!12-01 14:12:59.847 1398-1412/com.shymanzhu.architecture I/OpenGLRenderer: Initialized EGL, version 1.412-01 14:13:27.348 1398-1398/com.shymanzhu.architecture D/MainPresenter: onPause: 12-01 14:13:27.349 1398-1398/com.shymanzhu.architecture D/MainActivity: onPause: 12-01 14:13:28.265 1398-1398/com.shymanzhu.architecture D/MainPresenter: onStop: 12-01 14:13:28.266 1398-1398/com.shymanzhu.architecture D/MainActivity: onStop: 12-01 14:13:28.267 1398-1398/com.shymanzhu.architecture D/MainPresenter: onDestroy: 12-01 14:13:28.267 1398-1398/com.shymanzhu.architecture D/MainActivity: onDestroy:
到现在为止,是不是觉得对大体的框架和流程更加清楚了呢,其实这个Lifecycle组件就是一套设计模式中观察者模式的例子。按道理说,到这里也差不多结束了,具体的细节就是去跟源码了(既然这么说了,肯定有“但是”,机智如你),但是我觉得还是有必要将一些细节说明下。
部分细节
LifecycleOwner、LifecycleObserver、Lifecycle的定义都很简洁,就没必要列举出来了,我们从流程图可以看出来,我们要做的内容主要是实现LifecycleObserver接口,然后通过实现了LifecycleOwner接口的对象进行注册,以监听其生命周期,后续就是坐等通知了。
实现LifecycleObserver接口
从上面的类关系图,我们可以看到有三个接口GenericLifecycleObserver、FullLifecycleObserver、DefaultLifecycleObserver都直接或者间接继承了LifecycleObserver。然而GenericLifecycleObserver是隐藏的,我们用不了。那我们该怎么实现LifecycleObserver接口呢,有两种方式:
- 实现DefaultLifecycleObserver接口,然后重写里面生命周期方法;
- 直接实现LifecycleObserver接口,然后通过注解的方式来接收生命周期的变化;
对于这两种形式,Lifecycle.java文档中是建议使用第一种方式,因为文档中说明了,随着Java8成为主流,注解的方式会被弃用。
添加观察者
实现LifecycleOwner没什么好说的,我们直接看添加观察者( addObserver() ),LifecycleRegistry实现了Lifecycle接口,addObserver()实现如下:
@Override public void addObserver(@NonNull LifecycleObserver observer) { State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED; ObserverWithState statefulObserver = new ObserverWithState(observer, initialState); ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver); if (previous != null) { return; } LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); if (lifecycleOwner == null) { // it is null we should be destroyed. Fallback quickly return; } boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent; State targetState = calculateTargetState(observer); mAddingObserverCounter++; while ((statefulObserver.mState.compareTo(targetState) < 0 && mObserverMap.contains(observer))) { pushParentState(statefulObserver.mState); statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState)); popParentState(); // mState / subling may have been changed recalculate targetState = calculateTargetState(observer); } if (!isReentrance) { // we do sync only on the top level. sync(); } mAddingObserverCounter--; }
在该方法中主要关注一点:
- 根据observer和initialState构造ObserverWithState对象statefulObserver,然后将该对象存入mObserverMap,可以简单的把它理解成用来保存观察者的Map。
坐等通知
添加观察者后,那观察者接下来就是坐等通知了。从上面的时序图中,我们看到Fragment是通过LifecycleRegistry.handleLifecycleEvent()方法通知LifecycleRegistry其生命周期的,那我们看下LifecycleRegistry的handleLifecycleEvent()相关的方法。
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) { State next = getStateAfter(event);//计算下一个生命周期状态 moveToState(next); } private void moveToState(State next) { if (mState == next) { return; } mState = next; if (mHandlingEvent || mAddingObserverCounter != 0) { mNewEventOccurred = true; // we will figure out what to do on upper level. return; } mHandlingEvent = true; sync(); //看下sync()方法的具体实现 mHandlingEvent = false; } private void sync() { LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); if (lifecycleOwner == null) { Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch " + "new events from it."); return; } while (!isSynced()) { mNewEventOccurred = false; // no need to check eldest for nullability, because isSynced does it for us. //比较当前的生命周期和Map中第一个的观察者的生命周期,下面的另一个if语句类似。 if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) { backwardPass(lifecycleOwner); } Entry newest = mObserverMap.newest(); if (!mNewEventOccurred && newest != null && mState.compareTo(newest.getValue().mState) > 0) { forwardPass(lifecycleOwner); } } mNewEventOccurred = false; }//看下forwardPass()方法 private void forwardPass(LifecycleOwner lifecycleOwner) { Iterator> ascendingIterator = mObserverMap.iteratorWithAdditions(); while (ascendingIterator.hasNext() && !mNewEventOccurred) { Entry entry = ascendingIterator.next(); ObserverWithState observer = entry.getValue(); while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred && mObserverMap.contains(entry.getKey()))) { pushParentState(observer.mState); observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState)); popParentState(); } } } static class ObserverWithState { State mState; GenericLifecycleObserver mLifecycleObserver; ObserverWithState(LifecycleObserver observer, State initialState) { mLifecycleObserver = Lifecycling.getCallback(observer); mState = initialState; } void dispatchEvent(LifecycleOwner owner, Event event) { State newState = getStateAfter(event); mState = min(mState, newState); mLifecycleObserver.onStateChanged(owner, event); //通知观察者生命周期变化。 mState = newState; } }
- 代码中大体的细节就是这样子,当然更细的内容只能通过你自己去了解了,建议明白Lifecycle组件的框架和结构后,再去看它实现的细节,而不是一上来就深究代码,那让会让你只见树木不见森林。
0002LiveData
上一篇文章讲到了Android架构组件之一Lifecycle组件(Android 架构组件(一)——Lifecycle-Aware Components),现在我们再来看看另一个成员LiveData。
定义
简单地说,LiveData是一个数据持有类。它具有以下特点:
数据可以被观察者订阅;
能够感知组件(Fragment、Activity、Service)的生命周期;
只有在组件出于激活状态(STARTED、RESUMED)才会通知观察者有数据更新;
PS: 文中提到的“组件”皆指实现了LifecycleOwner接口Fragment、Activity。
为什么需要LiveData
从LiveData具有的特点,我们就能联想到它能够解决我们遇到的什么问题。LiveData具有以下优点:
能够保证数据和UI统一
这个和LiveData采用了观察者模式有关,LiveData是被观察者,当数据有变化时会通知观察者(UI)。
减少内存泄漏
这是因为LiveData能够感知到组件的生命周期,当组件处于DESTROYED状态时,观察者对象会被清除掉。
当Activity停止时不会引起崩溃
这是因为组件处于非激活状态时,不会收到LiveData中数据变化的通知。
不需要额外的手动处理来响应生命周期的变化
这一点同样是因为LiveData能够感知组件的生命周期,所以就完全不需要在代码中告诉LiveData组件的生命周期状态。
组件和数据相关的内容能实时更新
组件在前台的时候能够实时收到数据改变的通知,这是可以理解的。当组件从后台到前台来时,LiveData能够将最新的数据通知组件,这两点就保证了组件中和数据相关的内容能够实时更新。
针对configuration change时,不需要额外的处理来保存数据
我们知道,当你把数据存储在组件中时,当configuration change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。
资源共享
通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者,这个在继承LiveData中会看到具体的例子。
LiveData使用
在了解LiveData定义和优点后,那它到底怎么应用呢?LiveData有几种使用方式:
- 使用LiveData对象
- 继承LiveData类
使用LiveData对象
使用LiveData对象主要有以下几个步骤:
- 创建保存特定数据类型的LiveData实例;
- 创建Observer对象,作为参数传入LiveData.observe()方法添加观察者;
- 更新Livedata对象存储的数据;
创建LiveData实例
Android文档中建议LiveData配合ViewModel使用更加哦,其实呢,你也可以不使用ViewModel,但是一定要做到LiveData中保存的数据和组件分离,至于原因,前面我们已经提到过了。下面是在ViewModel中创建LiveData实例的例子:
public class NameViewModel extends ViewModel{ // Create a LiveData with a String private MutableLiveData mCurrentName; // Create a LiveData with a String list private MutableLiveData> mNameListData; public MutableLiveData getCurrentName() { if (mCurrentName == null) { mCurrentName = new MutableLiveData<>(); } return mCurrentName; } public MutableLiveData> getNameList(){ if (mNameListData == null) { mNameListData = new MutableLiveData<>(); } return mNameListData; }}
- 在NameViewModel中创建了两个MutableLiveData(MutableLiveData是LiveData的子类)实例,分别存储当前姓名、姓名列表;两个实例通过NameViewModel中的getter方法得到。
创建Observer对象,添加观察者
public class LiveDataFragment extends Fragment { private static final String TAG = "LiveDataFragment"; private NameViewModel mNameViewModel; @BindView(R.id.tv_name) TextView mTvName; public static LiveDataFragment getInstance(){ return new LiveDataFragment(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mNameViewModel = ViewModelProviders.of(this).get(NameViewModel.class); mNameViewModel.getCurrentName().observe(this,(String name) -> { mTvName.setText(name); Log.d(TAG, "currentName: " + name); }); // 订阅LiveData中当前Name数据变化,以lambda形式定义Observer mNameViewModel.getNameList().observe(this, (List nameList) -> { for (String item : nameList) { Log.d(TAG, "name: " + item); } }); // 订阅LiveData中Name列表数据变化,以lambda形式定义Observer } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.layout_livedata, container, false); ButterKnife.bind(this, view); return view; }}
- 在onCreate()方法中通过LiveData.observe()方法添加观察者,当数据变化时会通过回调方法通知观察者,在lambda表达式中更新当前姓名和打印姓名列表。
更新LiveData中的数据
在上面我们已经订阅了LiveData数据变化,现在我们看下如果LiveData数据变化时,上面的lambda表达式中是否会受到更新的通知。我们在LiveDataFragment中增加两个按钮来改变LiveData中的数据。
@OnClick({R.id.btn_change_name, R.id.btn_update_list})void onClicked(View view){ switch (view.getId()){ case R.id.btn_change_name: mNameViewModel.getCurrentName().setValue("Jane"); break; case R.id.btn_update_list: List nameList = new ArrayList<>(); for (int i = 0; i < 10; i++){ nameList.add("Jane<" + i + ">"); } mNameViewModel.getNameList().setValue(nameList); break; }}
- 代码很简单,在两个按钮的点击事件中通过LiveData.setValue()方法来改变LiveData中保存的数据。当点击这两个按钮的时候,我们会发现在onCreate()方法中会收相应到数据改变的回调。
继承LiveData类
除了直接使用LiveDatad对象外,我们还可以通过集成LiveData类来定义适合特定需求的LiveData。下面继承LiveData类的例子,验证下LiveData的其中一个优点——资源共享。
public class MyLiveData extends LiveData<Integer> { private static final String TAG = "MyLiveData"; private static MyLiveData sData; private WeakReference mContextWeakReference; public static MyLiveData getInstance(Context context){ if (sData == null){ sData = new MyLiveData(context); } return sData; } private MyLiveData(Context context){ mContextWeakReference = new WeakReference<>(context); } @Override protected void onActive() { super.onActive(); registerReceiver(); } @Override protected void onInactive() { super.onInactive(); unregisterReceiver(); } private void registerReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); mContextWeakReference.get().registerReceiver(mReceiver, intentFilter); } private void unregisterReceiver() { mContextWeakReference.get().unregisterReceiver(mReceiver); } private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG, "action = " + action); if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { int wifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); int wifiLevel = WifiManager.calculateSignalLevel( wifiRssi, 4); sData.setValue(wifiLevel); } } };}
- MyLiveData是个继承了LiveData的单例类,在onActive()和onInactive()方法中分别注册和反注册Wifi信号强度的广播。然后在广播接收器中更新MyLiveData对象。在使用的时候就可以通过MyLiveData.getInstance()方法,然后通过调用observe()方法来添加观察者对象,订阅Wifi信息强度变化。
- onActive(),此方法是当处于激活状态的observer个数从0到1时,该方法会被调用。
- onInactive() ,此方法是当处于激活状态的observer个数从1变为0时,该方法会被调用。
LiveDta原理
对于某个组件的原理解析,个人现在比较习惯于从类图、时序、源码几个方面着手分析。下面的内容也是从这几点依次展开的
类关系图
LiveData的类关系图相对比较简单,从上面的类图我们就能看到。和LiveData组件相关的类和接口有:LiveData类、Observer接口、GenericLifecycleObserver接口。LiveData类是个抽象类,但是它没有抽象方法,抽象类有个特点是:不能在抽象类中实例化自己。为什么LiveData会被定义成abstract而又没有抽象方法呢,这个…我也不知道,看了下LiveData的提交记录,是在将hasObservers()替换getObserverCount()方法时将LiveData改成了abstract,在此之前它是被定义为public,可以的可以看下这里的修改记录
MediatorLiveData继承自MutableLiveData,MutableLiveData继承自LiveData。MediatorLiveData可以看成是多个LiveData的代理,当将多个LiveData添加到MediatorLiveData,任何一个LiveData数据发生变化时,MediatorLiveData都会收到通知。
LiveData有个内部类LifecycleBoundObserver,它实现了GenericLifecycleObserver,而GenericLifecycleObserver继承了LifecycleObserver接口。在这里可以回顾下Lifecycle组件相关的内容。当组件(Fragment、Activity)生命周期变化时会通过onStateChanged()方法回调过来。
Observer接口就是观察者,其中定义了LiveData数据变化的回调方法onChanged()。
时序图
LiveData主要涉及到的时序有三个:
- 在Fragment/Activity中通过LiveData.observer()添加观察者(observer()方法中的第二个参数)。
- 根据Fragment/Activity生命周期发生变化时,移除观察者或者通知观察者更新数据。
- 当调用LiveData的setValue()、postValue()方法后,通知观察者更新数据。
源码解析
根据LiveData主要涉及到的三个时序的内容,我们通过源码看下它具体的实现。
添加观察者
LiveData提供了两种添加观察者的方法:observeForever()、observe()。
- observeForever()
@MainThreadpublic void observeForever(@NonNull Observer observer) { observe(ALWAYS_ON, observer);}
- 从方法的命名,我们也能对它的功能略知一二,通过observeForever()添加观察者,观察者会一直受到数据的变化回到,而不是在组件处于STARTED和RESUMED状态下才会收到,因为这是LifecycleOwner对象就不再是组件了,而是ALWAYS_ON;另外通过该方法添加观察者后,要手动调用removeObserver()方法来停止观察者接收回调通知。observeForever()方法体很简单,调用了observe()方法,传入的一个参数是ALWAYS_ON常量,重点看下ALWAYS_ON常量是个啥东东。
private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() { private LifecycleRegistry mRegistry = init(); private LifecycleRegistry init() { LifecycleRegistry registry = new LifecycleRegistry(this); registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); registry.handleLifecycleEvent(Lifecycle.Event.ON_START); registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME); return registry; } @Override public Lifecycle getLifecycle() { return mRegistry; }};
- ALWAYS_ON是LifecycleOwner常量,在init方法中会初始化Lifecycle的生命周期状态,完了之后,就没有改变过Lifecycle的生命周期状态了,这也就是为什么通过observeForever()添加观察者是,当数据改变时不管组件处于什么状态都会收到回调的原因,除非手动将观察者移除。
- observe()
@MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) { if (owner.getLifecycle().getCurrentState() == DESTROYED) { // ignore return; } //将LifecycleOwner对象和Observer对象封装成LifecycleBoundObserver对象。 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // mObservers可以理解成一个类似Map的容器,putIfAbsent()方法是判断容器中的observer(key) // 是否有已经和wrapper(value)关联,如果已经关联则返回关联值,否则关联并返回wrapper。 LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && existing.owner != wrapper.owner) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } owner.getLifecycle().addObserver(wrapper); //条件LifecycleOwner的生命周期观察者}
- 该方法也比较简单,主要逻辑都在注释中说明了,就不再赘述了。
组件(Fragment/Activity)生命周期发生变化
在LiveData.observe()方法中添加了组件(实现了LifecycleOwner接口的Fragment和Activity)生命周期观察者。而这个观察者就是LifecycleBoundObserver对象,下面我们看下LifecycleBoundObserver具体实现。
class LifecycleBoundObserver implements GenericLifecycleObserver { public final LifecycleOwner owner; public final Observer observer; public boolean active; public int lastVersion = START_VERSION; LifecycleBoundObserver(LifecycleOwner owner, Observer observer) { this.owner = owner; this.observer = observer; } @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { // LifecycleOwner对象生命周期发生变化时,会通过该回调方法通知过来。 // 如果当前处于Lifecycle.State.DESTROYED时,会自动将观察者移除。 if (owner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(observer); return; } // 判断是否处于actived状态,并将结果作为参数传递给activeStateChanged() activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState())); } void activeStateChanged(boolean newActive) { if (newActive == active) { //新状态和之前状态相同,不处理 return; } active = newActive; boolean wasInactive = LiveData.this.mActiveCount == 0; LiveData.this.mActiveCount += active ? 1 : -1; if (wasInactive && active) { //处于激活状态的observer个数从0到1 onActive(); } if (LiveData.this.mActiveCount == 0 && !active) { //处于激活状态的observer个数从1变为0 onInactive(); } if (active) { dispatchingValue(this); } }}
- 通过observe()方法添加观察者时,当组件(Fragment/Activity)生命周期发生变化时,onStateChanged()方法会被调用。如果通过observeForever()方法添加观察者时,只有在常量ALWAYS_ON初始化的时候,onStateChanged()方法会被调用三次(CREATED、STARTED、RESUMED),后面就不会收到DESTROYED的状态,这也就是为什么要通过removeObserver()方法手动移除观察者的原因。
onActive()和onInactive()都是空实现的方法,继承类可以选择去实现。我们看下dispatchingValue()方法的具体实现。
private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { // initiator不为空,考虑通知回调 considerNotify(initiator); initiator = null; } else { // initiator为空,考虑通知mObservers容器中对象回调 for (Iterator, LifecycleBoundObserver>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false;}private void considerNotify(LifecycleBoundObserver observer) { if (!observer.active) { return; } if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) { observer.activeStateChanged(false); return; } if (observer.lastVersion >= mVersion) { return; } observer.lastVersion = mVersion; // 最终回调的地方,也就是调用observe()或者observeForever()是传入的Observer对象。 observer.observer.onChanged((T) mData);}
改变LiveData数据
LiveData提供了两种改变数据的方法:setValue()和postValue()。区别是setValue()要在主线程中调用,而postValue()既可在主线程也可在子线程中调用。我们先看setValue()方法的具体实现:
@MainThreadprotected void setValue(T value) { assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常 mVersion++; mData = value; dispatchingValue(null);}
再看下postValue()方法的具体实现:
protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask = mPendingData == NOT_SET; mPendingData = value; } if (!postTask) { return; } // 会在主线程中执行 mPostValueRunnable中的内容。 ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); }private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue; synchronized (mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } // 在主线程中调用setValue()方法 setValue((T) newValue); }};
0003ViewModel
上一篇文章讲到了Android架构组件之LiveData(Android架构组件(二)——LiveData),现在我们再来看看另一个成员ViewModel。
ViewModel是什么?
ViewModel,从字面上理解的话,我们也能想到它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的。同时ViewModel还可以用来负责UI组件间的通信,这一点后面我们会有例子说明。
为什么需要ViewModel?
在看过前面两篇介绍Lifecycle和LiveData的内容后,这个问题的基本就不用回答了。既然ViewModel是用来管理和UI组件有关的数据的,而LiveData又是这些数据的持有类,所以在使用LiveData的时候,就自然想到要使用ViewModel了。另外从ViewModel的定义,我们也能看到ViewModel还可以用于UI组件间的通信,回想下当初面试官问你Fragment之间怎么通信的问题,掌握ViewModel后就多了一种高逼格的回答了。
ViewModel基本使用
前面在讲解LiveData时,我们已经使用了ViewModel,所以它的基本使用,在这里就不在赘述了,忘了的同学可以重新去翻一翻Android架构组件(二)——LiveData。我们主要看下ViewModel的生命周期,以及它用于组件间通信的使用。
ViewModel生命周期
我们看看ViewModel的生命周期,在官方文档中,用下面这张图来描述ViewModel的生命周期。
上图是用Activity作为例子,左侧表示Activity的生命周期状态,右侧绿色部分表示ViewModel的生命周期范围。当屏幕旋转的时候,Activity会被recreate,Activity会经过几个生命周期方法,但是这个时候ViewModel还是之前的对象,并没有被重新创建,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。
另外,有个注意的地方:在ViewModel中不要持有Activity的引用。为什么要注意这一点呢?从上面的图我们看到,当Activity被recreate时,ViewModel对象并没有被销毁,如果Model持有Activity的引用时就可能会导致内存泄漏。那如果你要使用到Context对象怎么办呢,那就使用ViewModel的子类AndroidViewModel吧。
用于组件间通信
下面我们看下ViewModel用于Fragment之间通信的例子。
public class CommunicateViewModel extends ViewModel { private static final String TAG = "CommunicateViewModel"; private MutableLiveData mNameLiveData; public LiveData getName(){ if (mNameLiveData == null) { mNameLiveData = new MutableLiveData<>(); } return mNameLiveData; } public void setName(String name){ if (mNameLiveData != null) { Log.d(TAG, "setName: " + name); mNameLiveData.setValue(name); } } @Override protected void onCleared() { super.onCleared(); mNameLiveData = null; }}
public class FragmentOne extends Fragment { private CommunicateViewModel mCommunicateViewModel; public static FragmentOne getInstance(){ return new FragmentOne(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_one, container, false); ButterKnife.bind(this, view); return view; } @OnClick(R.id.btn_set_name) void onViewClicked(View v){ switch (v.getId()){ case R.id.btn_set_name: mCommunicateViewModel.setName("Jane"); break; } }}
public class FragmentTwo extends Fragment { @BindView(R.id.tv_name) TextView mTvName; private CommunicateViewModel mCommunicateViewModel; public static FragmentTwo getInstance(){ return new FragmentTwo(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class); mCommunicateViewModel.getName().observe(this, name -> mTvName.setText(name)); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_two, container, false); ButterKnife.bind(this, view); return view; }}
代码逻辑很简单,首先定义一个CommunicateViewModel类,继承自ViewModel。然后定义两个Fragment:FragmentOne和FragmentTwo。在FragmentOne中改变CommunicateViewModel中LiveData保存的数据,然后在FragmentTwo中会收到数据改变的通知。这样一个过程就完成了FragmentOne和FragmentTwo之间的一次简单的通信。至于其中的原理,相信看过LiveData这篇文章话,这都不是问题了。在上面的例子中有个小陷阱:在初始化mCommunicateViewModel时,ViewModelProviders.of()方法传入的是Activity对象,如果你改成Fragment对象,那FragmentTwo里就只能傻等了,永远不会收到数据改变的通知。因为如果传给方法ViewModelProviders.of()的对象不同时,最终得到的就不是同一个ViewModel对象,这一点也可以通过打印两个Fragment中的mCommunicateViewModel验证。
实现原理
在讲ViewModel的原理时,我们还是延续前面的传统,先理清ViewModel相关的类图,然后理清ViewModel使用的时序图,最后就是根据时序图分析源码。
类图
废话不多说,直接上ViewModel相关的类图:
ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。
ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
类图要说明的内容就是以上几点了,对于UML类图中的关联、聚合、组合的关系,个人觉得比较难区分,在这里找到了一个比较好的回答What is the difference between association, aggregation and composition?
时序图
在使用ViewModel的例子中,也许你会察觉得到一个ViewModel对象需要的步骤有点多啊,怎么不直接new一个出来呢?在你看到ViewModel的类图关系后,你应该就能明白了,因为是会缓存ViewModel对象的。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
时序图看起来比较复杂,但是它只描述了两个过程:
- 得到ViewModel对象。
- HolderFragment被销毁时,ViewModel收到onCleared()通知。
读者朋友们可以根据这个时序结合源码看下具体的实现,这个也是我们下面要讲的内容——源码分析。
源码分析
根据上面时序图中的内容,我们从两个方面着手研究下ViewModel相关源码。
获取ViewModel对象
得到ViweModel对象,首先是通过ViewModelProviders.of()方法得到ViewModelProvider对象,然后调用ViewModelProvider.get()方法得到ViewModel对象。
一、得到ViewModelProvider对象
直接看ViewModelProviders类的具体实现
public class ViewModelProviders { @SuppressLint("StaticFieldLeak") private static DefaultFactory sDefaultFactory; private static void initializeFactoryIfNeeded(Application application) { if (sDefaultFactory == null) { sDefaultFactory = new DefaultFactory(application); } } 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; } @MainThread public static ViewModelProvider of(@NonNull Fragment fragment) { initializeFactoryIfNeeded(checkApplication(checkActivity(fragment))); return new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory); } @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { initializeFactoryIfNeeded(checkApplication(activity)); return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory); } @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) { checkApplication(checkActivity(fragment)); return new ViewModelProvider(ViewModelStores.of(fragment), factory); } @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @NonNull Factory factory) { checkApplication(activity); return new ViewModelProvider(ViewModelStores.of(activity), factory); } @SuppressWarnings("WeakerAccess") public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory { private Application mApplication; /** * Creates a {@code DefaultFactory} * * @param application an application to pass in {@link AndroidViewModel} */ public DefaultFactory(@NonNull Application application) { mApplication = application; } @NonNull @Override public T create(@NonNull Class 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); } }}
ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。我们在时序图中使用of(Fragment fragment)最为研究对象,这里我们也从该方法开始研究,在该方法中主要有两个步骤。
1,判断是否需要初始化默认的Factory
这一过程很简单:通过调用initializeFactoryIfNeeded()方法判断是否需要初始化mDefaultFactory变量。在此之前又会判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
2,创建一个ViewModelProvider对象。
创建ViewModel对象看似很简单,一行代码搞定new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)
。但是里面的里面的逻辑比较深,我们慢慢把它抽丝剥茧,看看有多深。
先看看ViewModelStores.of()方法
@MainThreadpublic static ViewModelStore of(@NonNull Fragment fragment) { return holderFragmentFor(fragment).getViewModelStore();}
又是一行代码,holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现
/** * @hide */@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)public static HolderFragment holderFragmentFor(Fragment fragment) { return sHolderFragmentManager.holderFragmentFor(fragment);}public ViewModelStore getViewModelStore() { return mViewModelStore; //返回ViewModelStore对象}
额,holderFragmentFor()又是一行代码,继续看HolderFragmentManager.holderFragmentFor()方法的具体实现
HolderFragment holderFragmentFor(Fragment parentFragment) { FragmentManager fm = parentFragment.getChildFragmentManager(); HolderFragment holder = findHolderFragment(fm); // 在activity和back stack中查找parentFragment if (holder != null) { return holder; } holder = mNotCommittedFragmentHolders.get(parentFragment); if (holder != null) { return holder; } // 注册Fragment生命周期回调 parentFragment.getFragmentManager() .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false); holder = createHolderFragment(fm); //创建HolderFragment对象。 mNotCommittedFragmentHolders.put(parentFragment, holder); return holder;}private FragmentLifecycleCallbacks mParentDestroyedCallback = new FragmentLifecycleCallbacks() { @Override public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) { super.onFragmentDestroyed(fm, parentFragment); HolderFragment fragment = mNotCommittedFragmentHolders.remove( parentFragment); if (fragment != null) { Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment); } } };private static HolderFragment createHolderFragment(FragmentManager fragmentManager) { HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象 fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss(); return holder;}public HolderFragment() { //这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用 setRetainInstance(true); }
在createHolderFragment()方法中,你也许发现调用FragmentTransaction.add()方法并没有传一个定义在layout文件中的id进去,而且你去看HolderFragment并没有重写Fragment.onCreateView()方法。这个有点反常,这是为什么呢? 这个疑惑可以从HolderFragment()构造方法中得到解答。在构造方法中调用了setRetainInstance(true)方法,该方法会使得HolderFragment所在的Activity被recreate时,HolderFragment不会被recreate,自然它就不会走onDestroy()、onCreate()方法。为什么不让HolderFragment被recreate呢?这里先卖个关子,等到在讲时序图中步骤二时我们再讲。
到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)
现在再看下ViewModelProvider的构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this.mViewModelStore = store; // 这个ViewStore对象实际上是HolderFragment中的ViewStore对象。}
二、得到ViewModel对象
在上面我们已经得到了ViewModelProvider对象,现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现
public T get(@NonNull Class 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);}@MainThreadpublic T get(@NonNull String key, @NonNull Class modelClass) { ViewModel viewModel = mViewModelStore.get(key); //从缓存中查找是否有已有ViewModel对象。 if (modelClass.isInstance(viewModel)) { //noinspection unchecked return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } viewModel = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。 mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel;}
ViewModelProvider.get()方法比较简单,该说的都在注释中写明了。最后我们看下ViewModelStore类的具体实现
public class ViewModelStore { private final HashMap 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(); }}
ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。
ViewModel收到onCleared()通知。
在前面我们提到,在HolderFragment的构造方法中调用了setRetainInstance(true);
目的是为了在其所在的Activity被recreate时,HolderFragment不会被recreate,为什么要这么做呢(之前面卖的关子),那就要看下它的onDestroy()方法了。
@Overridepublic void onDestroy() { super.onDestroy(); mViewModelStore.clear();}
在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。
总结
总结,额….还是由你们来完成,抛一个问题来作为总结吧:为什么ViewModel的生命周期比使用它的Activity/Fragment的生命周期更长呢? 如果你有认真看完这篇文章,我相信这个问题是难不倒你,如果没想到,那就多看几遍吧。最后,提供一张图来帮助你想这个问题。
0004Room
原文:https://blog.csdn.net/qq_24442769/article/details/79412408
添加Room依赖库
详细查看room配置
1.添加google的maven库,在project的gradle文件:
allprojects { repositories { jcenter() google() // 添加谷歌maven库 }}
2.添加架构组件依赖库,在module的gradle文件:
dependencies { // Room (use 1.1.0-alpha2 for latest alpha) implementation "android.arch.persistence.room:runtime:1.0.0" annotationProcessor "android.arch.persistence.room:compiler:1.0.0" // Test helpers for Room testImplementation "android.arch.persistence.room:testing:1.0.0"}
1.以上为gradle插件3.0
2.如果是kotlin项目,确保用kapt代替annotationProcessor,同时也要添加kotlin-kapt插件
apply plugin: 'kotlin-android'apply plugin: 'kotlin-kapt'apply plugin: 'kotlin-android-extensions'
3.为room添加rxjava支持库
dependencies { // RxJava support for Room (use 1.1.0-alpha1 for latest alpha) implementation "android.arch.persistence.room:rxjava2:1.0.0"}
4.Room @Dao查询中添加对Guava的Optional和ListenableFuture类型的支持。
dependencies { // Guava support for Room implementation "android.arch.persistence.room:guava:1.1.0-alpha2"}
5.和LiveData一起使用
// ReactiveStreams support for LiveData implementation "android.arch.lifecycle:reactivestreams:1.0.0"
Room三大组件
Room中有三个主要组件。
- Database: 用这个组件创建一个数据库。注解定义了一系列entities,并且类中提供一系列Dao的抽象方法,也是下层主要连接的访问点。注解的类应该是一个继承 RoomDatabase的抽象类。在运行时,你能通过调用Room.databaseBuilder()或者 Room.inMemoryDatabaseBuilder()获得一个实例
- Entity: 用这个组件创建表,Database类中的entities数组通过引用这些entity类创建数据库表。每个entity中的字段都会被持久化到数据库中,除非用@Ignore注解
DAO: 这个组件代表了一个用来操作表增删改查的dao。Dao 是Room中的主要组件,负责定义访问数据库的方法。被注解@Database的类必须包含一个没有参数的且返回注解为@Dao的类的抽象方法。在编译时,Room创建一个这个类的实现。
Entity类能够有一个空的构造函数(如果dao类能够访问每个持久化的字段)或者一个参数带有匹配entity中的字段的类型和名称的构造函数
如下代码片段包含一个简单的三大组件使用例子:
User.java
@Entitypublic class User { @PrimaryKey private int uid; @ColumnInfo(name = "first_name") private String firstName; @ColumnInfo(name = "last_name") private String lastName; // Getters and setters are ignored for brevity, // but they're required for Room to work.}
UserDao.java
@Daopublic interface UserDao { @Query("SELECT * FROM user") List getAll(); @Query(" SELECT * FROM user WHERE uid IN (:userIds)") List loadAllByIds(int[] userIds); @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") User findByName(String first, String last); @Insert void insertAll(User... users); @Delete void delete(User user);}
AppDatabase.java
@Database(entities = {User.class}, version = 1)public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao();}
通过以上文件,可以使用如下代码创建一个数据库实例:
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database-name").build();
生成数据库实例的具体操作:
Room.databaseBuilder(getApplicationContext(), RoomDemoDatabase.class, "database_name") .addCallback(new RoomDatabase.Callback() { //第一次创建数据库时调用,但是在创建所有表之后调用的 @Override public void onCreate(@NonNull SupportSQLiteDatabase db) { super.onCreate(db); } //当数据库被打开时调用 @Override public void onOpen(@NonNull SupportSQLiteDatabase db) { super.onOpen(db); } }) .allowMainThreadQueries()//允许在主线程查询数据 .addMigrations()//迁移数据库使用 .fallbackToDestructiveMigration()//迁移数据库如果发生错误,将会重新创建数据库,而不是发生崩溃 .build();
注意:初始化AppDatabase对象时必须遵守单例模式。因为每个RoomDatabase实例是相当昂贵的,并且几乎不需要访问多个实例。
Entity相关
当一个类被注解为@Entity并且引用到带有@Database 注解的entities属性,Room为这个数据库引用的entity创建一个数据表。
默认情况下,Room为每个定义在entity中的字段创建一个列。如果一个entity的一些字段不想持久化,可以使用@Ignore注解它们,像如下展示的代码片段:
@Entityclass User { @PrimaryKey public int id; public String firstName; public String lastName; @Ignore Bitmap picture;}
Entity的字段必须为public或提供setter或者getter。
Primary Key(主键)
每个entity必须定义至少一个字段作为主键。即使这里只有一个字段,仍然需要使用@PrimaryKey注解这个字段。并且,如果想Room动态给entity分配自增主键,可以设置@PrimaryKey的autoGenerate属性为true。如果entity有个组合的主键,你可以使用@Entity注解的primaryKeys属性,正如如下片段展示的那样:
@Entityclass User { public String firstName; public String lastName; @Ignore Bitmap picture; // 自增主键 @PrimaryKey(autoGenerate = true) public int id;}// 组合主键@Entity(primaryKeys = {"firstName", "lastName"})class User { public String firstName; public String lastName; @Ignore Bitmap picture;}
默认情况下,Room使用类名作为数据库的表名。如果希望表有一个不同的名称,设置@Entity注解的tableName属性,如下所示:
@Entity(tableName = "users")class User { ...}
注意: SQLite中的表名是大小写敏感的。
与tablename属性相似的是,Room使用字段名称作为列名称。如果你希望一个列有不同的名称,为字段增加@ColumnInfo注解,如下所示:
@Entity(tableName = "users")class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}
Indices and uniqueness(索引和唯一性)
public @interface Index { //定义需要添加索引的字段 String[] value(); //定义索引的名称 String name() default ""; //true-设置唯一键,标识value数组中的索引字段必须是唯一的,不可重复 boolean unique() default false;}
数据库索引可以加速数据库查询,@Entity的indices属性可以用于添加索引。在索引或者组合索引中列出你希望包含的列的名称。如下代码片段:
@Entity(indices = {@Index("name"), @Index("last_name", "address")})class User { @PrimaryKey public int id; public String firstName; public String address; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}
有时,表中的某个字段或字段组合需要确保唯一性,可以设置@Entity的@Index注解的unique属性为true。如下代码:
@Entity(indices = {@Index(value = {"first_name", "last_name"}, unique = true)})class User { @PrimaryKey public int id; @ColumnInfo(name = "first_name") public String firstName; @ColumnInfo(name = "last_name") public String lastName; @Ignore Bitmap picture;}
Relationships
SQLite是个关系型数据库,能够指明两个对象的关系。大多数ORM库支持entity对象引用其他的。Room明确的禁止这样。更多细节请参考Understand why Room doesn’t allow object references
public @interface ForeignKey { //引用外键的表的实体 Class entity(); //要引用的外键列 String[] parentColumns(); //要关联的列 String[] childColumns(); //当父类实体(关联的外键表)从数据库中删除时执行的操作 @Action int onDelete() default NO_ACTION; //当父类实体(关联的外键表)更新时执行的操作 @Action int onUpdate() default NO_ACTION; //在事务完成之前,是否应该推迟外键约束 boolean deferred() default false; //给onDelete,onUpdate定义的操作 int NO_ACTION = 1; int RESTRICT = 2; int SET_NULL = 3; int SET_DEFAULT = 4; int CASCADE = 5; @IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE}) @interface Action { }}
Room允许定义外键约束在两个entities。
例如:如果有一个entity叫book,你可以定义它和user的关系通过使用 @ForeignKey
注解,如下所示:
@Entity(foreignKeys = @ForeignKey(entity = User.class, parentColumns = "id", childColumns = "user_id"))class Book { @PrimaryKey public int bookId; public String title; @ColumnInfo(name = "user_id") public int userId;}
1.外键是十分强大的,因为它们允许你指明当引用的entity被更新后做什么。例如,如果相应的user实例被删除了,你可以通过包含@ForeignKey注解的onDelete=CASCADE属性让SQLite为这个user删除所有的书籍。
2.SQLite处理@Insert(OnConflict=REPLACE) 作为一个REMOVE和REPLACE操作而不是单独的UPDATE操作。这个替换冲突值的方法能够影响你的外键约束。
Nested objects
有时,希望entity中包含一个具有多个字段的对象作为字段。在这种情况下,可以使用@Embedded注解去代表一个希望分解成一个表中的次级字段的对象。接着你就可以查询嵌入字段就像其他单独的字段那样:
class Address { public String street; public String state; public String city; @ColumnInfo(name = "post_code") public int postCode;}
@Entityclass User { @PrimaryKey public int id; public String firstName; @Embedded public Address address;}
如上表示了一个包含如下名称列的user表:id,firstName,street,state,city,post_code。
注意:嵌入式字段还可以包含其他嵌入式字段
如果一个实体具有相同类型的多个内嵌字段,则可以通过设置前缀属性(prefix)使每个列保持惟一。然后将所提供的值添加到嵌入对象中每个列名的开头
@Embedded(prefix = "foo_") Coordinates coordinates;
Data Access Objects (DAOs)相关
Room的三大组件之一Dao(interface),以一种干净的方式去访问数据库。
注意: Room不允许在主线程中访问数据库。除非在建造器中调用allowMainThreadQueries(),可能会造成长时间的锁住UI。异步查询(返回LiveData或者RxJava流的查询)是从这个规则中豁免,因为它们异步的在后台线程中进行查询。
Insert
在Dao中创建一个方法并且使用@Insert注解它,Room生成一个在单独事务中插入所有参数到数据库中的实现。
如下代码展示了几个查询实例:
@Daopublic interface MyDao { @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(User... users); @Insert public void insertBothUsers(User user1, User user2); @Insert public void insertUsersAndFriends(User user, List friends);}
如果@Insert方法接收只有一个参数,它可以返回一个插入item的新rowId 的long值,如果参数是一个集合的数组,它应该返回long[]或者List
Update
@Update 是更新一系列entities集合、给定参数的惯例方法。它使用query来匹配每个entity的主键。如下代码说明如何定义这个方法:
@Daopublic interface MyDao { @Update public void updateUsers(User... users);}
尽管通常不是必须的,你能够拥有这个方法返回int值指示数据库中更新的数量。
@Insert, @Update都可以执行事务操作,定义在OnConflictStrategy注解类中
public @interface OnConflictStrategy { //策略冲突就替换旧数据 int REPLACE = 1; //策略冲突就回滚事务 int ROLLBACK = 2; //策略冲突就退出事务 int ABORT = 3; //策略冲突就使事务失败 int FAIL = 4; //忽略冲突 int IGNORE = 5;}
Delete
@Delete是一个从数据库中删除一系列给定参数的entities的惯例方法。它使用主键找到要删除的entities。如下所示:
@Daopublic interface MyDao { @Delete public void deleteUsers(User... users);}
尽管通常不是必须的,你能够拥有这个方法返回int值指示数据库中删除的数量。
Query
@Query 是DAO类中使用的主要注解,它允许你执行读/写操作在数据库中。每个@Query方法在编译时被校验,所以如果查询出了问题,将在编译时出现而不是运行时。
- 如果仅有一些字段匹配会警告
- 如果没有字段匹配会报错
查询示例:
@Daopublic interface MyDao { @Query("SELECT * FROM user") public User[] loadAllUsers();}
在编译时,Room知道这是查询user表中的所有列。如果查询包含语法错误,或者如果用户表不存在,Room在app编译时会报出合适的错误消息。
往查询中传入参数:
大多数时间,你需要传入参数到查询中去过滤操作,例如只展示比一个特定年龄大的用户,为了完成这个任务,在你的Room注解中使用方法参数,如下所示:
@Daopublic interface MyDao { @Query("SELECT * FROM user WHERE age > :minAge") public User[] loadAllUsersOlderThan(int minAge);}
当这个查询在编译器被处理,Room匹配:minAge绑定的方法参数。Room执行匹配通过使用参数名称,如果没有匹配到,在你的app编译期将会报错。
传入多个参数或者多次引用
如下所示:
@Daopublic interface MyDao { @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge") public User[] loadAllUsersBetweenAges(int minAge, int maxAge); @Query("SELECT * FROM user WHERE first_name LIKE :search " + "OR last_name LIKE :search") public List findUserWithName(String search);}
Returning subsets of columns(返回列中的子集)
多数时候,我们仅需要获取一个entity中的部分字段。例如,你的UI可能只展示user第一个和最后一个名称,而不是所有关于用户的细节。通过获取展示在UI的有效数据列可以使查询完成的更快。
只要查询中列结果集能够被映射到返回的对象中,Room允许你返回任何java对象。例如:
创建如下POJO通过拿取用户的姓和名。
public class NameTuple { @ColumnInfo(name="first_name") public String firstName; @ColumnInfo(name="last_name") public String lastName;}
@Daopublic interface MyDao { @Query("SELECT first_name, last_name FROM user") public List loadFullName();}
Room理解查询返回first_name和last_name的列值被映射到NameTuple类中。因此,Room能够生成合适的代码。如果查询返回太多columns,或者一个列不存在,Room将会报警。
Passing a collection of arguments
部分查询可能需要传入可变数量的参数,确切数量的参数直到运行时才知道。例如,想提取来自某个地区所有用户的信息。如下:
@Daopublic interface MyDao { @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)") public List loadUsersFromRegions(List regions);}
Observable queries
使用返回值类型为LiveData实现数据库更新时ui数据自动更新。
@Daopublic interface MyDao { @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)") public LiveData> loadUsersFromRegionsSync(List regions);}
RxJava
Room也能返回RxJava2 Publisher和Flowable对象,需添加android.arch.persistence.room:rxjava2 依赖。使用方法如下所示:
@Daopublic interface MyDao { @Query("SELECT * from user where id = :id LIMIT 1") public Flowable loadUserById(int id);}
Direct cursor access(直接游标访问)
如果你的应用逻辑直接访问返回的行,你可以返回一个Cursor对象从你的查询当中,如下所示:
@Daopublic interface MyDao { @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5") public Cursor loadRawUsersOlderThan(int minAge);}
注意:非常不建议使用Cursor API 因为它不能保证行是否存在或者行包含什么值。使用这个功能仅仅是因为你已经有期望返回一个cursor的代码并且你不能轻易的重构。
Querying multiple tables(联表查询)
一些查询可能访问多个表去查询结果。Room允许你写任何查询,所以你也能连接表格。
如下代码段展示如何执行一个根据借书人姓名模糊查询借的书的相关信息。
@Daopublic interface MyDao { @Query("SELECT * FROM book " + "INNER JOIN loan ON loan.book_id = book.id " + "INNER JOIN user ON user.id = loan.user_id " + "WHERE user.name LIKE :userName") public List findBooksBorrowedByNameSync(String userName); }
从这些查询当中也能返回POJOs,例如,可以写一个POJO去装载user和他们的宠物名称,如下:
@Daopublic interface MyDao { @Query("SELECT user.name AS userName, pet.name AS petName " + "FROM user, pet " + "WHERE user.id = pet.user_id") public LiveData> loadUserAndPetNames(); // You can also define this class in a separate file, as long as you add the // "public" access modifier. static class UserPet { public String userName; public String petName; }}
Using type converters (使用类型转换)
Room为原始类型和可选的装箱类型提供嵌入支持。然而,有时你可能使用一个单独存入数据库的自定义数据类型。为了添加这种类型的支持,你可以提供一个把自定义类转化为一个Room能够持久化的已知类型的TypeConverter。
例如:如果我们想持久化日期的实例,我们可以写如下TypeConverter去存储相等的Unix时间戳在数据库中:
public class Converters { @TypeConverter public static Date fromTimestamp(Long value) { return value == null ? null : new Date(value); } @TypeConverter public static Long dateToTimestamp(Date date) { return date == null ? null : date.getTime(); }}
一个把Date对象转换为Long对,另一个逆向转换,从Long到Date。因为Room已经知道了如何持久化Long对象,它能使用转换器持久化Date类型。
接着,你增加@TypeConverters注解到AppDatabase类
AppDatabase.java
@Database(entities = {User.java}, version = 1)@TypeConverters({Converter.class})public abstract class AppDatabase extends RoomDatabase { public abstract UserDao userDao();}
使用这些转换器,将实现使用自定义类型就像使用的原始类型,如下代码片段所示:
User.java
@Entitypublic class User { ... private Date birthday;}
UserDao.java
@Daopublic interface UserDao { ... @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to") List findUsersBornBetweenDates(Date from, Date to);}
还可以将@TypeConverter限制在不同的范围内,包含单独的entity,Dao和Dao中的 methods,具体查看官方文档。
Database migration(数据库迁移)
当添加或修改app后需要升级版本,中间可能修改了entity类。Room允许使用Migration类保留用户数据。每个Migration类在运行时指明一个开始版本和一个结束版本,Room执行每个Migration类的migrate()方法,使用正确的顺序去迁移数据库到一个最近版本。
注意:如果不提供必需的migrations类,Room重建数据库,意味着将丢失数据库中的所有数据。
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name") .addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, " + "`name` TEXT, PRIMARY KEY(`id`))"); }};static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE Book " + " ADD COLUMN pub_year INTEGER"); }};
输出模式
在编译时,将数据库的模式信息导出到JSON文件中,这样可有利于我们更好的调试和排错(DataBase的exportSchema = true)
module中的build.gradle
android { ... defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } }}
更多相关文章
- android加载字体内存泄漏的处理方法
- Android Studio运行main方法报错 SourceSet with name ‘main‘
- 谷歌开发工具android studio启动不了的解决方法
- android adb push 与 adb install的比较(两种安装APK的方法)
- 解决style attribute ‘@android:attr/windowEnterAnimation’ n
- Android 应用程序退出的四种方法
- Android绘图之Paint的使用方法详解
- Android Studio查看SQLite数据库方法大全