背景

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

本文分享下我们基于这个思想所开发的框架: 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 无需担心 Activity 已 Destroy 这种情况。

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

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

先说下这项优化的原理。

通常,我们会在 Activity 的 onCreate 里依次执行下面的代码:

@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优化效果

现在有了 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% 的优化,效果是非常不错的,而且代码也更加简洁清晰。

通过简单的注册 AutoLifecycle , Presenter 能够自动感知到所有 Lifecycle ,甚至包括自定义的特殊 Lifecycle ,如下图:

LifecycleAwarePresenter

优化三:MVP改进,让Presenter和View自动bind/unBind

第一项优化比较直接,可以先让大家形成一个直观印象。

我们项目是采用MVP项目,对于 Presenter 的使用存在一段固定代码,即在 onCreate 时调用bindView() ,在 onDestroy 时调用 unBindView() 。如下图:

public class OldActivity extends BaseActivity {

    BasePresenter mPresenter = new BasePresenter();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter.bindView(this); // onCreate时手动bind Presenter 和 IView
    }

    @Override
    protected void onDestroy() {
        mPresenter.unbindView(); // onDestroy时手动unBindView
        super.onDestroy();
    }
}

那么,既然我们现在能 让一个普通类自动感知Lifecycle ,那其实也就能让 Presenter 在感知到onCreate 时 自动bindView ,在感知到 onDestroy 时 自动unBindView 。

改进后的代码如下:

public class NewActivity extends BaseActivity {

    NewPresenter mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = new NewPresenter(this); // 只需要创建即可
    }
}

public class NewPresenter {

    private IView mIView;

    public NewPresenter(IView iView) {
        this.mIView = iView;
        // 向AutoLifecycle注册即可获得Lifecycle回调
        AutoLifecycle.getInstance().init(this);
    }

    // 当Activity进入onCreate后自动调用
    @AutoLifecycleEvent(activity = ActivityLifecycle.CREATE)
    private void onHostCreate() {
        bindView(mIView);
    }

    // 当Activity进入onDestroy后自动调用
    @AutoLifecycleEvent(activity = ActivityLifecycle.DESTROY)
    private void onHostDestroy() {
        unBindView();
    }
}

其实,在大家的平常开发中,还会存在许多类似 Presenter 的类: 需要在某个特定的Lifecycle下执行一些动作 。这时就可以基于 Lifecycle-Aware 来让这个普通类自动去执行,而不是去每个Activity/Fragment 里写一遍,提高类的内聚性。

AutoLifecycle的核心原理

(TL;DR)

下面介绍下 AutoLifecycle 的关键实现部分,感兴趣的读者可以参考。

1. 让Activity对外发送Lifecycle事件

使用过 RxJava 的同学知道里面有一个 PublishSubject,基于观察者模式,主动发送并接受消息。这里我们用 PublishSubject 来发送Lifecycle事件。见如下:

这里的Lifecycle事件可以自己定义,比如前面提到的PRE_INFLATE事件,是在setContentView之前发送,类似:

2. 感知某个Lifecycle的发生并自动执行回调

上面提了, PublishSubject 不仅能发送消息,还能接受自己的消息。基于这个特点,我们便可以监听每一个LifecycleEvent。如下图:

这里的参数Observable是我们希望被回调的函数,IContextLifecycle是指定的Lifecycle。即当指定的Lifecycle Event发生时,会自动subscribe提供的Observable。

基于这个功能,便可以实现上面场景一和场景二里的 @AutoLifecycleEvent 注解了,即把 @AutoLifecycleEvent 标注的函数包装成一个Observable,通过这个 executeOn 来注册函数的执行生命周期即可。

3. 监听Lifecycle并取消网络请求结果

在场景三里,我们为网络请求的 Observable 提供了一个 Transformer,它能在监听到某个Lifecycle发生时,停止数据流的向下流动。该 Transformer 的核心实现是:

可以看出,当指定的Lifecycle一旦发生,我们网络请求Observable就会停止向下传递数据。

4. 支持自定义Lifecycle,支持Activity/Fragment/DialogFrament等

可以看出, AutoLifecycle 除了支持常规的生命周期,还能支持自定义的特殊生命周期,比如View Inflate前。

另外,上面都是以Activity为例,不过显然这套框架可以灵活扩展,不局限于Activity,还能适用于Fragment、DialogFrament等。

总结

Lifecycle-Aware 思想是Google官方提出来的概念:赋予普通类自动感知生命周期的能力。而本文也是基于这个思想,提供了一些具体实践和优化的思路,读者同学可以根据自己的情况做更多的改进和尝试。java学习群669823128

让普通 Java 类自动感知 Activity Lifecycle的更多相关文章

  1. idea 新建java类自动补充创建人,创建时间,版本等..

    1.先进入 File 2.进入 Editor 找到 File and Code Templates 并点击 3.右侧点击 lncludes  4.第二项 File Header  /** * @aut ...

  2. eclipse debug模式下总是自动跳到ThreadPoolExecutor.java类

      1.情景展示 使用eclipse,debug模式运行项目时,总是会调用这个ThreadPoolExecutor.java类,明明没有打断点却自动停止运行. 2.原因分析 在eclipse中,默认是 ...

  3. mybatis怎样自动生成java类,配置文件?

    其实没有什么东西是可以自动生成的,只不过是别人已经写好了,你调用罢了. 所以想要mybatis自动生成java类,配置文件等,就必须要一些配置和一些jar包.当然这些配置也很简单. 为了有个初步的认识 ...

  4. Java基础进阶:APi使用,Math,Arrarys,Objects工具类,自动拆装箱,字符串与基本数据类型互转,递归算法源码,冒泡排序源码实现,快排实现源码,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    要点摘要 Math: 类中么有构造方法,内部方法是静态的,可以直接类名.方式调用 常用: Math.abs(int a):返回参数绝对值 Math.ceil(double a):返回大于或等于参数的最 ...

  5. 在IDEA中已经配置postgis数据库驱动并且能在Java类中连接数据库,但在servlet中无法连接数据库且导致Tomcat自动断开连接的解决方案

    最近在IDEA中用JDBC连接PostgreSQL数据库时遇到了这样一个奇怪的事情: 从PostgreSQL JDBC Driver官网下载好JDBC驱动之后,在IDEA的Project Struct ...

  6. IDEA Java 类注释、方法注释模板(可实现自动参数使用生成)

    JAVA 类文件注释设置 设置地方: 模板 /** * <p> * $description * </p> * * @author Tophua * @since ${DATE ...

  7. jvm系列(一):java类的加载机制

    java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...

  8. Java类初始化

    Java类初始化 成员变量的初始化和构造器 如果类的成员变量在定义时没有进行显示的初始化赋值,Java会给每个成员变量一个默认值 对于  char.short.byte.int.long.float. ...

  9. 深入研究Java类装载机制

    目录 1.为什么要研究java类装在机制? 2.了解类装载机制,对于我们在项目开发中有什么作用? 3.装载实现细节. 4.总结 一.为什么药研究Java类装载机制 java类加载机制,便于我们使用自定 ...

随机推荐

  1. Spring 5:以函数式方式注册 Bean

    http://www.baeldung.com/spring-5-functional-beans 作者:Loredana Crusoveanu 译者:http://oopsguy.com 1.概述 ...

  2. Coin Change (II)(完全背包)

                                                               Coin Change (II) Time Limit: 1000MS   Mem ...

  3. 1289 大鱼吃小鱼 1305 Pairwise Sum and Divide 1344 走格子 1347 旋转字符串 1381 硬币游戏

    1289 大鱼吃小鱼 有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右.游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼.从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右 ...

  4. javascript的数值转换 number()详解

    ---恢复内容开始--- number() parseInt() parseFloat()这三个函都可以把数非数值转换为数值,我们看看他们的区别在哪里 一 Number() 转型函数Number()是 ...

  5. dets

    模块说明 提供基于文件的项式存储,项式以元组表示,其中某个位置为键,默认第1位置 Dets为Mniesia所用,后者增加了事务.查询.和分布式支持. Dets文件不能超过2GB. Dets只有set ...

  6. Python系列之模块、和字符串格式化

    Python 模块 模块让你能够有逻辑地组织你的Python代码段. 把相关的代码分配到一个 模块里能让你的代码更好用,更易懂. 模块也是Python对象,具有随机的名字属性用来绑定或引用. 模块分为 ...

  7. win7下安装Ubuntu后进不去win7的解决方法

    win7下安装Ubuntu后进不去win7的解决方法 刚刚给同学在win7下安装了Ubuntu16.04,结果在安装完后竟然无法在电脑重启后,找到win7的进入选项. 在网上找了找,都不行!就差点重装 ...

  8. Coursera_程序设计与算法_计算导论与C语言基础_数组应用练习

    您也可以在我的个人博客中阅读此文章:跳转 编程题#1:求字母的个数 描述 在一个字符串中找出元音字母a,e,i,o,u出现的次数. 输入 输入一行字符串(字符串中可能有空格,请用gets(s)方法把一 ...

  9. Linq标准查询操作符

     Linq的出现让代码简洁了不少.之前在项目中基本都在使用它,但是没有完整的整理过,今天借这个周末,将其进行整理,方便后期对其的使用.Linq的操作可以分为聚合,连接,转换,元素操作符,相等操作,生成 ...

  10. word2vec 数学原理

    word2vec 是 Google 于 2013 年推出的一个用于获取词向量的开源工具包.我们在项目中多次使用到它,但囿于时间关系,一直没仔细探究其背后的原理. 网络上 <word2vec 中的 ...