长久以来,Android官方并没有指定一个项目架构的规范,只要能够实现功能,代码怎么编写都是你的自由,但是不同的人技术水平不同,最终编写出来的代码质量是千差万别的。
由于没有官方规范,为了追求更高的代码质量,慢慢的就有第三方的社区和开发者将一些更加高级的项目架构引入到了Android平台上,如MVP,MVVM等。使用这些架构开发出来的应用程序在代码质量、可读性、易维护性等方面都有更加出色的表现,于是这些架构渐渐成为主流。
后来Google意识到了这个情况,在2017年推出了一个官方的架构组件库,Architecture Components,旨在帮助开发者编写出更加符合高质量代码规范,更具有架构设计的App。2018年,Google又推出了一个全新的开发组件工具集Jetpack,并将Architecture Components作为Jetpack的一部分纳入其中,未来的Jetpack还在不断的继续扩充。
🔴Jetpack中的组件有一个特点,他们大部分不依赖于任何Android系统版本,这意味着这些组件通常是定义在AndroidX库当中的,并且拥有非常好的向下兼容性。来看一下Jetpack全家福## ViewModel
专门用于存放与界面相关的数据
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。
🔴ViewModel的生命周期
🎴简单使用
添加依赖
implementation ‘androidx.lifecycle:lifecycle-extensions:2.2.0’
比较好的规范是给每一个Activity和Fragment都创建一个对应的ViewModel,所有与界面相关的数据都应该放在ViewModel中,在这里我们实现一个计数器的共呢给,在ViewModel中加入一个Counter变量用于计数。布局文件非常简单,一个TextView用于显示当前的计数,一个Button用于对计数器加一,一个Button用于对计数器清零。
class MainViewModel : ViewModel(){
var counter = 0
}
class MainActivity : AppCompatActivity(){
lateinit var viewModel : MainViewModel
override fun onCreate(savedInstanceState : Bundle?){
...
viewModel = ViewModelProvider(this).get(MainViewModel::class.java);
plusOneBtn.setOnClickListner{
viewModel.counter++;
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter(){
infoText.test = viewModel.counter.toString()
}
}
需要注意的是我们绝对不可以直接去创建ViewModel实例,而一定要通过ViewModelProvider。
因为ViewModel有其独立的生命周期,并且其生命周期长于Activity。如果我们在onCreat方法中创建ViewModel,那么每次onCreate方法执行的时候,ViewModel都会创建一个新的实例,这样当手机屏幕发生旋转时,就无法保留其中的数据了。
🚗向ViewModel传递参数
MainViewModel的构造函数中没有任何参数,如果我们向通过构造函数传递一些参数该怎么办呢。
需要借助ViewModelProvider.Factory
现在的计数器虽然在屏幕旋转的时候不会丢失数据,但是如果退出程序之后再重新打开,那之前的数据就会被清零了。我们需要再退出程序的时候对当前数据进行保存,然后再重新打开程序的时候读取之前保存的计数,并传递给MainViewModel。修改MainViewModel:
class MainViewModel(countReserved: Int) : ViewModel(){
var counter = countReserved
}
我们给MainViewModel的构造函数中添加了一个参数,用于记录之前保存的值。
接下来如如何向MainViewModel构造函数中传参?
class MainViewModelFactory(private val countReserved: Int) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T{
return MainViewModel(countReserved) as T
}
}
新建MainViewModelFactory类,让他实现ViewModelProvider.Factory接口,可以看到他的构造函数中也接受了一个countReserved参数。另外 ViewModelProvider.Factory接口要求实现create方法,方法中我们创建了MainViewModel的实例,并且把countReserved参数传了进去。
为什么这里就可以直接创建MainViewModel的实例呢,因为create方法的执行时机和Activity的生命周期无关,所以不会产生之前的问题。
最后修改MainActivity:
class MainActivity : AppCompatActivity(){
lateinit var viewModel : MainViewModel
lateinit var sp:SharedPreferences
override fun onCreate(savedInstanceState : Bundle?){
...
sp = getPreferences(Context.MODE_PRIVATE)
Val countReserved = sp.getInt("count_Reserved",0)
viewModel = ViewModelProvider(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java);
plusOneBtn.setOnClickListner{
viewModel.counter++;
refreshCounter()
}
clearBtn.setOnClickListner{
viewModel.counter = 0
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter(){
infoText.test = viewModel.counter.toString()
}
override fun onPause(){
...
sp.edit{
putInt("count_Reserved",viewModel.counter)
}
}
}
Lifecycles
在编写app时,可能会遇到需要感知Activity生命周期的情况。比如某个界面发起了一条网络请求,但是当请求得到响应时界面已经关闭了,这个时候就不应该继续对响应的结果进行处理。
在一个Activity中感知他的它的生命周期非常简单,但如果要在一个非Activity类中呢?
📌书上介绍了手写监听器的方式,这里就不记录了
Lifecycles能够让任何一个类都轻松感知到Activity的生命周期,同时又不用在Activity中编写大量的逻辑处理。
class MyObserver : LifecycleObserver{
}
新建了一个MyObserver类,实现LifecycleObserver接口,LifecycleObserver是一个空方法接口。
接下来我们可以在MyObserver中定义任何方法,但如果想要感知到activity的生命周期,需要借助额外的注解功能。
class MyObserver : LifecycleObserver{
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun activityStart(){
...
}
}
我们在方法上使用了@OnLifecycleEvent注解,并传入了一种生命周期事件,生命周期事件共有7种:ON_CREATE ON_START ON_RESUME ON_PAUSE ON_STOP ON_DESTROY分别对应Activity的生命周期,还有一种ON_ANY,可以对应任何生命周期。
现在还是无法工作的,因为Activity的生命周期发生变化的时候没有人去通知MyObserver,而我们又不想在Activity中一个个手动通知。
借助LifecycleOwner可以轻松实现:
LifecycleOwner.getLifecycle().addObserver(MyObserver())
activity、fragment本身就是LifecycleOwner,因此我们在Activity就可以这样写:
lifecycle.addObserver(MyObserver())
🏓目前MyObserver虽然能够感知到activity的生命周期发生了变化,却没有办法主动或之当前的activity的生命周期。要解决这个问题,需要在MyObserver的构造函数中将Lifecycle对象传进来
class MyObserver(var lifecycle: Lifecycle) : LifecycleObserver{
}
之后我们就可以在任何地方调用lifecycle.currentState来主动获得activity的生命周期。lifecycle.currentState有五种状态:INITIALIZED ,DESTROYED , CREATED, STARTED , RESUMED
## LiveData
LiveData大部分情况下都是使用在ViewModel中的。
上面我们编写的计数器其实是存在问题的,目前的逻辑是:每当按下plus按钮,都会先给viewmodel中的计数加一,然后立即获取最新的计数。这种方式在单线程模式下确实可以正常工作,但如果viewmodel的内部开启了线程去执行一些耗时逻辑,那么在点击按钮后立即去获取最新的数据,得到的肯定还是之前的数据。
于是发现,我们一直都是用的是Activity中手动获取viewmodel中的数据这种交互方式,但是viewmodel却无法把数据的变化主动通知给activity。
获取你会说把activity的实例传给viewmodel,这样view model不就能主动对activity进行通知了吗。注意,千万不能这么做!!!viewmodel的生命周期是长于activity的,如果把activity的实例传给viewmodel,很有可能会因为activity无法释放而造成内存泄漏。
这个问题的解决方案就是使用LiveData。**LiveData可以包含任何类型的数据,并在数据发生变化的时候通知给观察者。**如果我们将计数器的计数用LiveData来包装,然后再activity中去观察他,就可以主动将数据变化通知给activity了。
特性
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 activity 和 fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 activity 和 fragment 的生命周期被销毁时,系统会立即退订它们)。
如果观察者的生命周期处于非活跃状态(如返回堆栈中的 activity),它便不会接收任何 LiveData 事件。
//粘性事件
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
//数据倒灌
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
使用
1.创建LiveData对象,一般结合ViewModel使用,在ViewModel中创建
我们发现LiveData是一个抽象类,它的默认实现子类是MutableLiveData,但是看源码他们没有区别~唯一区别就是set和post方法公开了,之所以这么设计,是考虑到单一开闭原则,只有拿到 MutableLiveData 对象才可以发送消息,LiveData 对象只能接收消息,避免拿到 LiveData 对象时既能发消息也能收消息的混乱使用。
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> currentSecond;//表示包含的是整形数据
public MutableLiveData<Integer> getCurrentSecond() {
if(currentSecond == null){
currentSecond = new MutableLiveData<>();
currentSecond.setValue(0);
}
return currentSecond;
}
}
2.在Activity中使用
public class MainActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
textView.setText(String.valueOf(viewModel.getCurrentSecond().getValue()));
viewModel.getCurrentSecond().observe(this, new Observer<Integer>() {
/**
* 在这里更新UI,每次setValue或者postValue时会回调到这个方法
*/
@Override
public void onChanged(Integer i) {
textView.setText(String.valueOf(i));
}
});
startTimer(); //开启定时任务
}
/*
* 子线程定时周期任务,每隔一秒更新数据
*/
private void startTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//非UI线程 postValue
//UI线程 setValue
viewModel.getCurrentSecond().postValue(viewModel.getCurrentSecond().getValue()+1);
}
},1000,1000);
}
}
运行结果:数据每秒都会加一
常见方法
setValue->主线程更新value
postValue->子线程更新value
getValue->获取值
observe-> 第一个参数是LifecyclerOwner(是livedata监听生命周期的关键),第二个参数传入一个Observer接口的实例化对象(需重写onchanged方法,更新UI的逻辑写到这个方法中)