可感知生命周期组件——开始面向组件编程之旅
1.背景
现有APP开发所面临问题:
- 自定义的控件/组件无生命周期感知,容易造成内存难以回收
- 代码访问安全性(使用已经被回收的资源)
- 代码过分依赖四大组建
- 组件间通信
- 开发事件驱动源太多(入口非单一)
2.面向组件开发的利器
Android Jetpack
Architecture components
Architecture components 是一系列Android库用来帮助开发者以一种鲁棒,可测试,可维护的方式来架构自己APP。
-
DataBinding
数据双向的绑定,获取xml对应的实体,并加入一些简单语法 -
LifeCycles
通过对Activity生命周期监听,赋予组件等效的生命周期 -
LiveData
监听mode的变化并通知注册者 -
ViewModel
以注重生命周期的方式管理界面相关的数据——连接view和model的桥梁
3.LifeCycles赋予自定义组件生命周期
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
3.1作用
- 给组件赋予生命周期
- 查询关联的页面生命周期
- 构建周期观察者
3.2栗子
//官网demo
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
* desc:常用的示例
* Time:2019-07-17
* Author:Gavin
*/
public class LifecycleWidget extends ViewGroup implements LifecycleObserver {
Lifecycle mLifecycle;
public LifecycleWidget(Context context) {
super(context);
if (context instanceof LifecycleOwner) {//历史原因LifecycleRegistryOwner被废弃
mLifecycle = ((LifecycleOwner) context).getLifecycle();
mLifecycle.addObserver(this);
}
postDelayed(() -> {
if (mLifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {//请对比之前的处理方式(更多的时候没有处理)
//dosth.
}
}, 1500);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onStart() {
//LogUtil.d(this.getClass().getSimpleName() + " onStart");
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private void onPause() {
//LogUtil.d(this.getClass().getSimpleName() + " onPause");
}
/**
* 处理view销毁的业务逻辑
*/
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
//LogUtil.d(this.getClass().getSimpleName() + " onDestroy");
}
}
一般情况下,我们只要拿到了Activity中的Lifecycle(如context)就可以进行周期注册的监听了,当然v4中的Fragment
也可以进行相应的生命周期监听。
//ReportFragment#dispatch
private void dispatch(Lifecycle.Event event) {
Activity activity = getActivity();
if (activity instanceof LifecycleRegistryOwner) {
((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
return;
}
if (activity instanceof LifecycleOwner) {
Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
}
}
}
3.3原理
image.png- SupportActivity继承LifecycleOwner,并持有LifecycleRegistry
- SupportActivity实例化 LifecycleRegistry mLifecycleOwner主要用来注册周期监听器,并负责通知、加锁、状态变更等操作,主要的作用是将周期分发给注册的对象,如自定义组件。
- SupportActivity实例化 ReportFragment ——ReportFragment.injectIfNeededIn(this);—— ReportFragment LifecycleRegistry将周期通过SupportActivity中LifecycleRegistry分发。
编译期间注解生成的代码
image.png
image.png
4. LiveData监听Model的变化并通知View
LiveData 是可观察数据的持有者。它可感知组件的生命周期(lifecycle aware)。可感知组件生命周期的意思是,LiveData 仅仅在生命周期期间可被观察(通过lifecycle自行解绑定
),更准确地说是在 Activity 和 Fragment 的生命周期中。通过 Activity 和 Fragment 的引用,LiveData 知道 UI 是处于 onScreen 状态还是 offScreen 状态或者是被销毁状态。将 UI 对象传给 LiveData,无论何时数据发生变化,它都会通知 lifecycle owner,并且让 UI 重绘。
4.1作用
-
没有内存泄漏:Observers 跟 Lifecycle 绑定,当关联的对象销毁后 LiveData 也会被清理
不直接持有View层代码
- 不会让处于停止状态的 Activity 崩溃:即 Activity 在 back stack 中时,不会收到 LiveData 事件流
- 数据永远保持最新:一旦处于活跃状态时总能收到最新数据
- 不必手动处理组件生命周期:Observers 只需要专注于观察相关的数据,而不必操作是否要停止观察或开始观察。LiveData 底层会处理这些细节
- 保证 UI 跟数据同步:生命周期状态变化时 LiveData 会通知观察者。比起每次在数据变化后去更新 UI,观察者可以在有变更后主动更新 UI
- 正确处理 configuration 变化:当观察者由于 configuration 变化而重建时,比如屏幕旋转,它马上可以收到最新的可用数据
- 共享资源:可以继承 LiveData,并使用单例模式来包装 system service,以例在 app 内共享
implementation "android.arch.lifecycle:extensions:$current_version"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
注意:当我们从 Observer 接收新的值时,是运行在主线程中的。尽管是从后台线程发送新的值,但底层会帮我们进行线程切换以方便开发者使用。
4.2栗子
//ViewModel
public class CreditCardBankDetailViewModel extends ViewModel {
//ViewModel解耦:view和model
// Activity/Fragment/ViewHolder(view主要是xml)等view层对LiveData订阅,当model改变时,LiveData将会立即通知订阅者
//所以view ViewModel没有直接持有view的一个实例化代理Observer
private MutableLiveData<BankDetail> mBankDetail;
public MutableLiveData<BankDetail> getLiveData() {
if (mBankDetail == null) {//1.实例化一个LiveData,用来监听数据层 BankDetail 的改变
mBankDetail = new MutableLiveData<>();
}
return mBankDetail;
}
public void load(String bank_id) {
//2.model层进行数据加载,不管是网络、文件、数据库、cache、sp等
Repository.apiBus.bankdetailV102(bank_id)
.subscribe(new JtSingObserver<BankDetail>() {
@Override
public void onSuccess(BankDetail data) {
//3.1通知订阅者
getLiveData().setValue(data);
}
@Override
public void onError(Throwable e) {
//3.2通知订阅者,即使是null
getLiveData().setValue(null);
UIUtil.INSTANCE.showExceptionMsg(e);
}
});
}
}
//View
CreditCardBankDetailViewModel mModel;
mModel = ViewModelProviders.of(this).get(CreditCardBankDetailViewModel.class);
mModel.getLiveData().observe(this, new Observer<BankDetail>() {
@Override
public void onChanged(@Nullable BankDetail bean) {
//回调里面的处理不用担心页面被回收的问题,因为那时订阅早已经断开了,只关心业务
if (bean == null) {
mLoadingHelper.showNetworkError();
return;
}
mLoadingHelper.showContentView();
headerView = new BankDetailHeaderView(getActivity(), bean.baseInfo);
headerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.WRAP_CONTENT));
RecyclerViewUtils.setHeaderView(recycleView, headerView);
mAdapter.initList(bean.list);
getTitleBar().getCenterText().setText(bean.baseInfo.bankName);
getTitleBar().getCenterText().setTextColor(ContextCompat.getColor(getActivity(), R.color
.font_bar_title));
getTitleBar().getCenterText().setAlpha(0.0f);
}
});
4.3原理
- 如果监听数据的变化(和大多数的MVVM框架一样)
- 如何监听View的生命周期(代理+注解)
- 如何处理configuration的变化(这个不重要,但是可以了解下Fragment#mRetainInstance,依赖ViewModel)
5.引入ViewModel的开发模型
5.1作用
作为APP内页面数据的载体,用于连接liveData和UI(Fragment/Activity)
- 解决配置发生修改数据丢失
- 解决Fragment之间、Fragment和Activity之前值传递
- 解决数据清理工作
- 解决夸页面Activity直接数据共享(AndroidViewModel)
- 作为数据绑定的桥梁
5.2栗子
//通过一下方式获取viewModel,可以在Activity/Fragment/component间通信且共享数据,viewModel提供clear方法用于释放持有的资源
CreditCardBankDetailViewModel mModel = ViewModelProviders.of(this).get(CreditCardBankDetailViewModel.class);
5.3原理
反射实例化对象 HolderFragment
HolderFragment持有ViewModelStore(HashMap<String, ViewModel> )
ViewModel的生命周期(最新版中加入了持久化支持)
image.png6总结
image.png6.1编程指导意见
编程是一种创造性的劳动,开发Android app也不例外。同一问题有许多种解决办法,不管是activity或者fragment之间的数据交互,还是获取远程数据并缓存到本地,还是一个有一定规模的app可能遇到的其它常见问题。
manifest定义的入口-activity, service, broadcast receiver等等,都不是数据源,它们只应该和与该入口相关的数据协作。
app不同模块之间要有明确的责任边界。比如,不要把加载网络数据的代码分散到不同的类或者包中。同样,也不要把不相关的职责(比如数据缓存和数据绑定)放在一个类中。
每个模块暴露的细节越少越好。不要图一时爽快把模块的内部实现暴露出来。
在定义模块之间的交互时,思考如何做到各个模块的独立和可测试。
app的核心应该可以让它脱颖而出,不要花时间重复造轮子和写相同代码。相反,你应该把自己的精力集中到如何使app独一无二上。重复的工作就交给Android Architecture Components以及推荐的库来处理吧。
尽可能的持久化重要的,新生的数据,以便让你的app在离线状态下都可用。虽然你可能享受着高速的网络,但你的用户也许不是。
repository应该指派一个可以用作单一可信任源(single source of truth)。
网友评论