Neclues是一款基于MVP框架的Android Library, 它合理地将程序中的可视化界面和后台线程联系起来。
如果你还不了解MVP框架,请点击查看详情
框架介绍
我们在应用中使用MVP和RxJava来代替Loader和AsyncTask已经有一段时间了,但即便是这些高级框架也难免会有一些缺陷:
- 当状态改变的时候程序很难再继续执行一个后台任务
- 当应用重启后程序可以恢复我们的可视化界面,但是对于后台任务却难以继续执行
当大多数程序都不具备该功能的时候,这种缺陷看起来更像是一个bug。当我们乘坐地铁上操作手机的时候由于网络太慢而切换到另外一个程序,这时候由于程序状态的改变可能会引起一些bug,但是大多数开发者都没有考虑到。
安卓开发文档对此类问题描述的非常详细,详情请查看Processes and Threads - 4. Background process :“如果我们正确的重写了activity的生命周期方法,并保存了当前的状态,那么当用户杀掉进程的时候不会对用户体验造成不好的影响,因为当用户再次打开应用程序后activity会恢复它的可视化状态”。
然而,以上所说并非正确,因为程序被kill的时候我们并没有保存后台任务,程序会记得他被杀死的时候界面是什么样子,但是不会记得它当时在做什么,从而会对用户体验造成影响。例如:一个程序在恢复了一个ProgressBar View,但是没有恢复它的后台任务,这时候我们就会看到一个“ProgressBar Forever” bug。
Neclues的特点
- Neclues自动地将view和后台任务绑定在一起,即便是在状态恢复的时候程序也不会忘记View和View绑定的任务状态
- 程序即便是在低内存设备上运行或长久地停留在后台,Neclues也能将后台任务正确地唤醒
- Neclues框架遵循了The Kiss Principle原则,人人都能轻易地理解
- Neclues不依赖于Dagger,你不需要为了注入Presenter而重写一个类
- Presenter在Neclues中是一个不依赖于View的外部类,这可以自动屏蔽activity内存泄漏的问题
历史演变
在第一个版本中,Neclues主要是为了尽可能地简化MVP的实现
过了几个月后,作则开始意思到RxJava已经变成了执行后台任务的首选框架,所以RxPresenter诞生了。从那以后,框架目的变成了对RxJava的支持
作者一开始对于生命周期的正确处理是非常明确的,所以在文档中没有着重强调。随着越来越多的MVP框架出现,Necleus需要区分与它们的不同,所以框架的描述变成了:“Neclues是一款基于MVP框架的Android Library, 它合理地将程序中的可视化界面和后台线程联系起来”
重启后台任务的方式
Necleus提供了3种恢复后台任务的方式,restartableFirst,restartableLatestCache,restartableReplay. 下面我们看一下它们之间“从Presenter将后台任务结果发送到View”的区别
restartableFirst只会提交第一个数据
restartableLatestCache只会提交最新的数据,而且会将它缓存起来,每当View重新Attach到Presenter的时候都会重新提交
restartableReplay提交所有的数据,每当View重新Attach到Presenter的时候都会重新提交
基础样例
这个例子是在Presenter创建的时候从网络上获取数据
public class MainPresenter extends RxPresenter<MainActivity> {
private static final int REQUEST_ITEMS = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(REQUEST_ITEMS,
() -> App.getServerAPI()
.getItems()
.observeOn(AndroidSchedulers.mainThread()),
(activity, response) -> activity.onItems(response.items),
(activity, throwable) -> activity.onItemsError(throwable));
// After a process restart, it will restart itself when registering the restartable
if (savedState == null)
start(REQUEST_ITEMS);
}
}
从View中获取参数
我们在MVP框架中使用网络请求的时候可能会在View中获取一些参数,在Necleus中你只需要调用getPresenter().request("my_parameter");
public class MainPresenter extends RxPresenter<MainActivity> {
private static final int REQUEST_ITEMS = 1;
private static final String NAME_KEY = MainPresenter.class.getName() + "#name";
private String name;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// Recover the value after a process restart, you have to restore it before
// registering the restartable, or you will get a NullPointerException
if (savedState != null)
name = savedState.getString(NAME_KEY);
restartableLatestCache(REQUEST_ITEMS,
() -> App.getServerAPI()
.getItems(name)
.observeOn(AndroidSchedulers.mainThread()),
(activity, response) -> activity.onItems(response.items),
(activity, throwable) -> activity.onItemsError(throwable));
}
@Override
protected void onSave(@NonNull Bundle state) {
super.onSave(state);
state.putString(NAME_KEY, name);
}
public void request(String name) {
this.name = name;
start(REQUEST_ITEMS);
}
}
Neclues中Activity的使用
public class MainPresenter extends RxPresenter<MainActivity> {
private static final int REQUEST_ITEMS = 1;
private static final String NAME_KEY = MainPresenter.class.getName() + "#name";
private String name;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// Recover the value after a process restart, you have to restore it before
// registering the restartable, or you will get a NullPointerException
if (savedState != null)
name = savedState.getString(NAME_KEY);
restartableLatestCache(REQUEST_ITEMS,
() -> App.getServerAPI()
.getItems(name)
.observeOn(AndroidSchedulers.mainThread()),
(activity, response) -> activity.onItems(response.items),
(activity, throwable) -> activity.onItemsError(throwable));
}
@Override
protected void onSave(@NonNull Bundle state) {
super.onSave(state);
state.putString(NAME_KEY, name);
}
public void request(String name) {
this.name = name;
start(REQUEST_ITEMS);
}
}
从View中发送数据(此处用到了IcePick)
为了避免每次都手动调用save/restore instance,你可以在应用中使用IcePick,并创建BasePresenter
public class BasePresenter<V extends ViewWithPresenter> extends RxPresenter<V> {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
Icepick.restoreInstanceState(this, savedState);
}
@Override
protected void onSave(@NonNull Bundle state) {
super.onSave(state);
Icepick.saveInstanceState(this, state);
}
}
如果你的Presenter继承了BasePresenter,那么应该长这样
import icepick.State;
public class MainPresenter extends BasePresenter<MainActivity> {
private static final int REQUEST_ITEMS = 1;
@State String name;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(REQUEST_ITEMS,
() -> App.getServerAPI()
.getItems(name)
.observeOn(AndroidSchedulers.mainThread()),
(activity, response) -> activity.onItems(response.items),
(activity, throwable) -> activity.onItemsError(throwable));
}
public void request(String name) {
this.name = name;
start(REQUEST_ITEMS);
}
}
依赖包引入
基本引入
dependencies {
compile 'info.android15.nucleus:nucleus:6.0.0'
}
如果需要使用NucleusSupportFragment
, NucleusFragmentActivity,请引入
dependencies {
compile 'info.android15.nucleus:nucleus-support-v4:6.0.0'
}
如果需要使用NucleusAppCompatActivity
,请引入
dependencies {
compile 'info.android15.nucleus:nucleus-support-v7:6.0.0'
}
混淆配置
-keepclassmembers class * extends nucleus.presenter.Presenter{
<init>();
}
对于RxJava2而言
dependencies {
compile 'info.android15.nucleus5:nucleus:7.0.0'
}
如果需要使用NucleusSupportFragment
, NucleusFragmentActivity,请引入:
dependencies {
compile 'info.android15.nucleus5:nucleus-support-v4:7.0.0'
}
如果需要使用NucleusAppCompatActivity
,请引入:
dependencies {
compile 'info.android15.nucleus5:nucleus-support-v7:7.0.0'
}
混淆配置
-keepclassmembers class * extends nucleus5.presenter.Presenter {
<init>();
}
注:你也可以拷贝这些类到你个项目中使用