让普通 Java 类自动感知 Activity Lifecycle

《Android 技术进阶》专栏文章目录:
《Java 技术之反射》
《Java 技术之动态代理机制》
《Java 技术之类加载机制》
《Java 技术之垃圾回收机制》
《Java 技术之线程间通信与同步》
《带你学开源项目之 OkHttp:自己动手实现okhttp》
《带你学开源项目:LeakCanary-如何检测 Activity 是否泄漏》
《带你学开源项目:RxLifecycle-当 Activity 被 destory 时自动停掉网络请求》
《让普通 Java 类自动感知 Activity Lifecycle》

正文

小专栏读者可查看底部《源码》部分

背景

在 Android 开发中,我们都很熟悉 Activity 的 Lifecycle,并且会在特定的 Lifecycle 下执行特定的操作。当然,我们清楚 Lifecycle 本身是带有 Android 特质的,那尝试设想下,如果普通的 Java Class 也能自动感知 Lifecycle 呢?咋一听这个想法似乎背后意义不大,但在实际探索中,我们发现这个特性能为我们达成一些之前未考虑到或者不易实现的优化。

本文分享下我们基于这个思想所开发的框架:AutoLifecycle 及其带来的一些有意思的实践。

  • 优化一:当Activity进入onDestroy时,自动取消网络请求返回
  • 优化二:自动将网络请求时机提前到View渲染之前,提高页面打开速度
  • 优化三:MVP改进,让Presenter和View自动bind/unBind

注:下文提到的Lifecycle-Aware就是这里指代的让普通 Java Class 自动获取 Lifecycle

实践及优化

优化一:当Activity进入onDestroy时,自动取消网络请求返回

在网络请求时,相信大家都有一个经验:在每个网络结果回来时,我们做的第一件事不是显示数据,而是写个if-else判断Activity还在不在。

mTopApiObservable
  ...
  .subscribe(new Subscriber<Object>() {
      @Override
      public void onNext(Object data) {
        if(activity == null) {
            return; // 判断Activity是否还在,不在就不去显示数据
        }

        display(data); // 显示数据
      }
      ...
  });

由于网络请求都是异步的,所以不得不做这样的判断,来防止不可预测的空指针问题或内存泄漏问题。

既然你总是担心Activity还在不在,那么如果我们通过Lifecycle-Aware让每个网络请求能自动感知Activity的onDestroy事件
并在onDestroy时,自动把网络请求结果取消掉不再返回,那就能够消除这个担忧了。

mTopApiObservable
  ...
  .compose(bindUntilEvent(ActivityLifecycle.DESTROY)) // 绑定Activity的onDestroy事件
  .subscribe(new Subscriber<Object>() {
      @Override
      public void onNext(Object data) {
        display(data); // 直接去显示数据
      }
      ...
  });

其中最关键的就是compose(bindUntilEvent(ActivityLifecycle.DESTROY))这句,它能达到的效果是:一旦Activity发生onDestroy时,Observer的数据就会停止向Subscriber里流动。从而保证onNext无需担心ActivityDestroy这种情况。

在上面网络请求的实践里,你还可以根据自己的情况把Destroy换成Stop/Pause等,而且可以看出,这种自动取消机制可适用于任何Observable,不仅仅是网络请求。

优化二:自动将网络请求提前到View Inflate之前,加速页面渲染

先说下这项优化的原理。
通常,我们会在ActivityonCreate里依次执行下面的代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);   // Inflate View
    findViewByIds();                // 初始化View
    presenter.loadDataFromServer(); // 发起网络请求
}

即在Inflate View初始化View之后,才发起网络请求去加载数据。

而实际上,网络请求是不占用主线程的,如果能在Inflate View之前就在其他线程发起网络请求,可以把整个页面显示耗时缩短100ms-200ms

LoadBeforeInflate优化效果

LoadBeforeInflate优化效果

现在有了AutoLifecycle框架,我们就可以很轻松实现:让Presenter自动监听Inflate View这个生命周期,在那时发起网络请求即可。

public class NewPresenter {

    public NewPresenter(IView iView) {
        ...
        // 向AutoLifecycle注册
        AutoLifecycle.getInstance().init(this); 
    }

    // 当Activity Inflate View前自动回调
    @AutoLifecycleEvent(activity = ActivityLifecycle.PRE_INFLATE)
    private void onHostPreInflate() {
         loadDataFromServer(); // 发起网络请求
    }
    ...
}

此时,我们的Activity也不用手动调用presenter.loadDataFromServer();了,因为Presenter内会在感知到Inflate View事件时自动发起网络请求。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.XXX);
    findViewByIds();
    // 无需手动启动网络请求
}

经过测试,在保证单个网络请求耗时相同的情况下,页面从onCreate显示数据的渲染耗时可以从550ms缩短到367ms,也就是30%-40%的优化,效果是非常不错的,而且代码也更加简洁清晰。

top Created with Sketch.