让普通 Java 类自动感知 Activity Lifecycle
背景
在 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的更多相关文章
- idea 新建java类自动补充创建人,创建时间,版本等..
1.先进入 File 2.进入 Editor 找到 File and Code Templates 并点击 3.右侧点击 lncludes 4.第二项 File Header /** * @aut ...
- eclipse debug模式下总是自动跳到ThreadPoolExecutor.java类
1.情景展示 使用eclipse,debug模式运行项目时,总是会调用这个ThreadPoolExecutor.java类,明明没有打断点却自动停止运行. 2.原因分析 在eclipse中,默认是 ...
- mybatis怎样自动生成java类,配置文件?
其实没有什么东西是可以自动生成的,只不过是别人已经写好了,你调用罢了. 所以想要mybatis自动生成java类,配置文件等,就必须要一些配置和一些jar包.当然这些配置也很简单. 为了有个初步的认识 ...
- Java基础进阶:APi使用,Math,Arrarys,Objects工具类,自动拆装箱,字符串与基本数据类型互转,递归算法源码,冒泡排序源码实现,快排实现源码,附重难点,代码实现源码,课堂笔记,课后扩展及答案
要点摘要 Math: 类中么有构造方法,内部方法是静态的,可以直接类名.方式调用 常用: Math.abs(int a):返回参数绝对值 Math.ceil(double a):返回大于或等于参数的最 ...
- 在IDEA中已经配置postgis数据库驱动并且能在Java类中连接数据库,但在servlet中无法连接数据库且导致Tomcat自动断开连接的解决方案
最近在IDEA中用JDBC连接PostgreSQL数据库时遇到了这样一个奇怪的事情: 从PostgreSQL JDBC Driver官网下载好JDBC驱动之后,在IDEA的Project Struct ...
- IDEA Java 类注释、方法注释模板(可实现自动参数使用生成)
JAVA 类文件注释设置 设置地方: 模板 /** * <p> * $description * </p> * * @author Tophua * @since ${DATE ...
- jvm系列(一):java类的加载机制
java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...
- Java类初始化
Java类初始化 成员变量的初始化和构造器 如果类的成员变量在定义时没有进行显示的初始化赋值,Java会给每个成员变量一个默认值 对于 char.short.byte.int.long.float. ...
- 深入研究Java类装载机制
目录 1.为什么要研究java类装在机制? 2.了解类装载机制,对于我们在项目开发中有什么作用? 3.装载实现细节. 4.总结 一.为什么药研究Java类装载机制 java类加载机制,便于我们使用自定 ...
随机推荐
- base64码转图片
1将图片转换为Base64编码,可以让你很方便地在没有上传文件的条件下将图片插入其它的网页.编辑器中. 这对于一些小的图片是极为方便的,因为你不需要再去寻找一个保存图片的地方. 2.假定生成的代码为& ...
- 打印ASCII码
总时间限制:1000ms内存限制:65536kB 描述 输入一个除空格以外的可见字符(保证在函数scanf中可使用格式说明符%c读入),输出其ASCII码. 输入 一个除空格以外的可见字符. 输出 一 ...
- Gradient Boost 算法流程分析
我们在很多Gradient Boost相关的论文及分析文章中都可以看到下面的公式: 但是,对这个公式的理解,我一直也是一知半解,最近,终于下决心对其进行了深入理解. 步骤1:可以看作优化目标的损失函数 ...
- 程序员网站开发时应该注意的SEO问题
一.链接的统一性 搜索引擎排名最主要的因素就是网站内容和链接,假如网站内部链接不一致,在很大程度上直接影响着网站在搜索引擎中的排名.例如彩票专营店导航栏中的“首页”链接,程序员在开发时可能会有以下几种 ...
- Log4j按级别输出日志到不同文件配置分析 (转:projava)
关于LOG4J 按照级别输出日志,并按照级别输出到不同文件中的说法有很多, 网上贴的最多的log4j.properties的设置是这样的 log4j.rootLogger=info,stdout,in ...
- zoj1871steps 数学 水
zoj1871 题目大意 ...
- sqlDependency监控数据库数据变化,自动通知
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 让asp.net网站支持多语言,使用资源文件
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs&quo ...
- Sqlite常用sql语句
sqlite常用sql语句 --返回UTC时间 select CURRENT_TIMESTAMP; --返回本地时间 select datetime(CURRENT_TIMESTAMP,'localt ...
- thinking in java 随笔
初始化顺序 在一个类里,初始化的顺序是由变量在类内的定义顺序决定的.即使变量定义大量遍布于方法定义的中间,那些变量仍会在调用任何方法之前得到初始化--甚至在构建器调用之前.例如: class Tag ...