可感知生命周期组件——开始面向组件编程之旅

可感知生命周期组件——开始面向组件编程之旅

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
  1. SupportActivity继承LifecycleOwner,并持有LifecycleRegistry
  2. SupportActivity实例化 LifecycleRegistry mLifecycleOwner主要用来注册周期监听器,并负责通知、加锁、状态变更等操作,主要的作用是将周期分发给注册的对象,如自定义组件。
  3. 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 mBankDetail;

    public MutableLiveData 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() {
                    @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() {
            @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

ViewModel的生命周期(最新版中加入了持久化支持)
image.png

6总结

image.png
6.1编程指导意见

编程是一种创造性的劳动,开发Android app也不例外。同一问题有许多种解决办法,不管是activity或者fragment之间的数据交互,还是获取远程数据并缓存到本地,还是一个有一定规模的app可能遇到的其它常见问题。

manifest定义的入口-activity, service, broadcast receiver等等,都不是数据源,它们只应该和与该入口相关的数据协作。

app不同模块之间要有明确的责任边界。比如,不要把加载网络数据的代码分散到不同的类或者包中。同样,也不要把不相关的职责(比如数据缓存和数据绑定)放在一个类中。

每个模块暴露的细节越少越好。不要图一时爽快把模块的内部实现暴露出来。

在定义模块之间的交互时,思考如何做到各个模块的独立和可测试。

app的核心应该可以让它脱颖而出,不要花时间重复造轮子和写相同代码。相反,你应该把自己的精力集中到如何使app独一无二上。重复的工作就交给Android Architecture Components以及推荐的库来处理吧。

尽可能的持久化重要的,新生的数据,以便让你的app在离线状态下都可用。虽然你可能享受着高速的网络,但你的用户也许不是。

repository应该指派一个可以用作单一可信任源(single source of truth)。

参考

  • 关于可感知生命周期组件
  • Handling Lifecycles with Lifecycle-Aware Components
  • Android Jetpack

你可能感兴趣的:(可感知生命周期组件——开始面向组件编程之旅)