感谢你的再次光临,欢迎来到Android Architecture Components(ACC)系列文章。上篇文章咱们一块儿讨论了Room,经过Room咱们可以方便的操做App的数据库。若是你的App对本地数据库有所依赖的话,Room你值得拥有。java
今天这篇文章继续上篇文章的步伐,让咱们一块儿来全面了解ACC另外一强大的组件LiveData。相信你立刻会喜欢上她!😍😍😍android
LiveData是一种可观测数据容器,它会在数据变化时通知观测器,以便更新页面;同时它具有生命感知能力,能够实时观察Activity/Fragment的生命周期状态。git
既然它是可观察数据容器与具有生命感知能力,那么它的优势也很明显,能够概括与如下几点github
当咱们要监听某一个数据的变化时,LiveData将大显身手。例如界面数据的更新,当数据发生变化时,咱们要通知界面进行更新ui,这时咱们可使用LiveData在当前Activity/Fragment中对该数据注册一个观察者,实时监听数据的任何改动。每一次改动LiveData都会发送通知给观察者。数据库
另外一方面,LiveData感知界面的生命周期,因此只有在界面生命周期的STARTED或者RESUMED状态才会通知观察者。若是你一直处于后台且数据一直在变化,LiveData是不会发生通知,只有在界面再一次回到前台,这时LiveData才会发生通知且只会发送一次,数据的更新取的是最后一次的变化数据。这样能够有效的避免内存泄露与ui不存在时致使的NullPointerExceptionsegmentfault
首页咱们须要在咱们的app下的build.gradle中添加以下依赖代码api
dependencies { def lifecycle_version = "1.1.1" // ViewModel and LiveData implementation "android.arch.lifecycle:extensions:$lifecycle_version" // alternatively - just LiveData implementation "android.arch.lifecycle:livedata:$lifecycle_version" annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" }
而后咱们就能正式使用LiveData,看以下代码:app
class ContactsViewModel(application: Application, private val defTitle: String = "Contacts") : AndroidViewModel(application) { val message: MutableLiveData<String> by lazy { MutableLiveData<String>() } val contactsList: MutableLiveData<List<ContactsModel>> = MutableLiveData() fun getContacts(refresh: Boolean): LiveData<List<ContactsModel>> { message.value = "" if (refresh) { getDataFromRemote() } else if (contactsList.value == null || contactsList.value?.size ?: 0 <= 0) { message.value = "数据请求中,请稍后!" if (mLocalData.isEmpty()) { getDataFromLocal() } } return contactsList } private fun getDataFromLocal() { val runnable = Runnable { val dao = mContactsDao.getAllContacts() if (dao.isNotEmpty()) { contactsList.postValue(dao) } else { getDataFromRemote() } } mExecutors.disIoExecutor.execute(runnable) } private fun getDataFromRemote() { Handler().postDelayed({ contactsList.value = mRemoteData mLocalData = mRemoteData saveContacts(mRemoteData) Thread(Runnable { title.postValue("Remote Contacts") }).start() message.value = "数据加载完成~" }, MDELAY_MILLIS) } }
首先咱们使用MutableLiveDat对咱们所须要的数据进行了包裹,MutableLiveData它继承与LiveData,暴露了postValue()与setValue()方法。一旦MutableLiveData所包裹的数据发生变化,咱们能够经过postValue()(asynchronously)与setValue()(synchronously)来设置值与发送通知,告诉观察者数据已经改变。async
在 getDataFromLocal()方法中,咱们使用了Room来操做数据库,同时直接经过返回LiveData数据类型的数据,使得Room与LiveData完美结合。
因此咱们再来看看观察者的代码:ide
class ContactsActivity : AppCompatActivity() { private lateinit var mViewModel: ContactsViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_contacts_layout) setupViewModel() } private fun setupViewModel() { mViewModel = ViewModelProviders.of(this, ContactsFactory.getInstance(application))[ContactsViewModel::class.java] //active STARTED、RESUMED mViewModel.getContacts(true).observe(this, Observer { //todo ... }) mViewModel.message.observe(this, Observer { //todo ... }) } }
咱们为所须要观察的数据添加了observer方法,该方法第一个参数是LifecyleOwner,以便让LiveData具备生命感知能力,这里要感知的是ContactsActivity,因此传入this便可。第二个参数是一个回调方法,一旦数据发生变化它的onChanged()就会回调,并将数据带回,这样界面就能实时更新数据。
最后因为LiveData是生命感知的因此咱们也无需担忧他的register/unregister
咱们已经知道LiveData会对处于STATERD或者RESUMED状态进行发送通知,若是该状态下存在observer,由无到有,咱们称之为active,反正称之为inactive。若是咱们可以知道什么时候为active与什么时候为inactive,那么咱们就能够实现本身的LiveData。为了解决这个问题,LiveData提供了两个方法,分别为onActive()与onInactive()。
例如咱们想为一个监听器实现生命感知能力,能够进行以下操做
public class StockLiveData extends LiveData<BigDecimal> { private static StockLiveData sInstance; private StockManager mStockManager; private SimplePriceListener mListener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; @MainThread public static StockLiveData get(String symbol) { if (sInstance == null) { sInstance = new StockLiveData(symbol); } return sInstance; } private StockLiveData(String symbol) { mStockManager = new StockManager(symbol); } @Override protected void onActive() { mStockManager.requestPriceUpdates(mListener); } @Override protected void onInactive() { mStockManager.removeUpdates(mListener); } }
一旦observer由无到有,那么咱们就在onActive()方法中进行监听器的注册。observer由有到无,咱们能够在onInactive()中进行注销。这样就能够是咱们的监听器具有生命感知能力。避免没必要要的内存泄露或者一次crash。同时一旦监听器的回调方法生效时,咱们又能够经过LiveData的setValue()来对观察者进行数据的更新。因此观察者的代码以下:
public class MyFragment extends Fragment { @Override public void onActivityCreated(Bundle savedInstanceState) { StockLiveData.get(getActivity()).observe(this, price -> { // Update the UI. }); } }
若是细心的话,能够发现上面的StockLiveData已经实现了监听器共享。咱们能够在多个界面中使用StockLiveData进行添加observer。例如在Activity中,只要有一个observer,那么它将一直监听数据的变化。
案例:对于App统计需求,一旦涉及到多个页面间的统计参数传递,能够自定义一个扩展LiveData来全局监听参数的传递与变化。
在通知观察者数据改变以前,若是你想改变LiveData中的值类型,可使用Transformations
获取原有类型中的某个特定的类型值,能够比喻为解包,可使用map()方法
LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
与map对应的是switchMap()方法,这里就是打包。
private LiveData<User> getUser(String id) { ...; } LiveData<String> userId = ...; LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
与LiveData相关的还有一个MediatorLiveData,它的做用是:能够同时监听多个LiveData。例如同时监听本地数据与远程数据。
LiveData<List<User>> usersFromDatabase; LiveData<List<User>> usersFromNetwork; MediatorLiveData<List<User>> usersLiveData = new MediatorLiveData<>(); usersLiveData.addSource(usersFromDatabase, newUserList -> usersLiveData.setValue(value)); usersLiveData.addSource(usersFromNetwork, newUserList -> usersLiveData.setValue(value));
一旦其中一个发送变化,MediatorLiveData都会发送通知给observer。
是否感受LiveData很强大呢?那么赶忙行动起来吧,让你的App中数据也具备可观察与生命感知能力。
最后文章中的代码均可以在Github中获取到。使用时请将分支切换到feat_architecture_components
Android Architecture Components Part1:Room
Android Architecture Components Part3:Lifecycle
Android Architecture Components Part4:ViewModel