RxJava掉坑
RxJava 注意
Observable.just()、fromIterable() 的局限性
代码执行过早
使用 Observable.just() 即使你没有调用 subscribe 方法。just() 括号里面的代码也已经执行了。显然,Observable.just() 不适合封装网络数据,因为我们通常不想在 subscribe 之前做网络请求。
同理,fromIterable
也和 just
有同样的缺点。当然,这个可以简单的用 defer()/fromCallable()/create() 操作符来是实现只有 subscribe 只有才加载。
Observable.just() 不够灵活
设计模式上我们追求 “Minimize Mutability” 但是如果我们的程序越来越 reactive 的时候。一个 ObservableJust 往往是不满足需求的。比如之前一定订阅的 subscriber。如果数据更新了,你不可以同过 ObservableJust 来通知所有的 Observable 新数据更新了,需要你的 subscriber 主动更新。这显然有悖于我们追求的 reactive programming。 主动 pull 数据而不是数据告诉你,我更新了然后再做出反应。
当然 ObservableJust 在很多情况下,确实不错。如果你不需要监听后续的更新,那么 ObservableJust 可以满足你的需求。
RxJava2 通用的 Observer,需要在 onNext 捕获异常
防止使用框架的人崩溃
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public abstract class BaseNetObserver<T> implements Observer<T> {
private static final int OTHER_EXCEPTION_CODE = -1;
@Override
public void onSubscribe(Disposable d) {
}
@Override
public final void onNext(T t) {
if (t != null) {
try {
onSuccess(t);
} catch (@NonNull Exception e) {
onError(new RuntimeException(e));
}
} else {
onError(new RuntimeException("返回的数据为null"));
}
}
@Override
public void onError(Throwable e) {
if (e instanceof ApiException) {
onFailed((ApiException) e);
} else {
onFailed(new ApiException(e, OTHER_EXCEPTION_CODE));
}
}
@Override
public void onComplete() {
}
protected abstract void onSuccess(@NonNull T t);
/**
* 错误回调
*/
protected abstract void onFailed(ApiException ex);
}
RxJava2 SchedulerPoolFactory 周期清除
前一阵在用 Android Studio 的内存分析工具检测 App 时,发现每隔一秒,都会新分配出 20 多个实例,跟踪了一下发现是 RxJava2 中的 SchedulerPoolFactory 创建的。
一般来说如果一个页面创建加载好后是不会再有新的内存分配,除非页面有动画、轮播图、EditText 的光标闪动等页面变化。当然了在应用退到后台时,或者页面不可见时,我们会停止这些任务。保证不做这些无用的操作。然而我在后台时,这个线程池还在不断运行着,也就是说 CPU 在周期性负载,自然也会耗电。那么就要想办法优化一下了。
SchedulerPoolFactory 的作用是管理 ScheduledExecutorServices 的创建并清除。
SchedulerPoolFactory 部分源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static void tryStart(boolean purgeEnabled) {
if (purgeEnabled) {
for (;;) { // 一个死循环
ScheduledExecutorService curr = PURGE_THREAD.get();
if (curr != null) {
return;
}
ScheduledExecutorService next = Executors.newScheduledThreadPool(1, new RxThreadFactory("RxSchedulerPurge"));
if (PURGE_THREAD.compareAndSet(curr, next)) {
// RxSchedulerPurge线程池,每隔1s清除一次
next.scheduleAtFixedRate(new ScheduledTask(), PURGE_PERIOD_SECONDS, PURGE_PERIOD_SECONDS, TimeUnit.SECONDS);
return;
} else {
next.shutdownNow();
}
}
}
}
static final class ScheduledTask implements Runnable {
@Override
public void run() {
for (ScheduledThreadPoolExecutor e : new ArrayList<ScheduledThreadPoolExecutor>(POOLS.keySet())) {
if (e.isShutdown()) {
POOLS.remove(e);
} else {
e.purge();//图中154行,purge方法可用于移除那些已被取消的Future。
}
}
}
}
我查了相关问题,在 stackoverflow 找到了此问题 RxSchedulerPurge always take periodic cpu load even in the background on Android
,同时也给 RxJava 提了 Issue,得到了回复是可以使用:
1
2
// 修改周期时间为一小时
System.setProperty("rx2.purge-period-seconds", "3600");
当然你也可以关闭周期清除:
1
System.setProperty("rx2.purge-enabled", "false");
作用范围如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static final class PurgeProperties {
boolean purgeEnable;
int purgePeriod;
void load(Properties properties) {
if (properties.containsKey(PURGE_ENABLED_KEY)) {
purgeEnable = Boolean.parseBoolean(properties.getProperty(PURGE_ENABLED_KEY));
} else {
purgeEnable = true; // 默认是true
}
if (purgeEnable && properties.containsKey(PURGE_PERIOD_SECONDS_KEY)) {
try {
// 可以修改周期时间
purgePeriod = Integer.parseInt(properties.getProperty(PURGE_PERIOD_SECONDS_KEY));
} catch (NumberFormatException ex) {
purgePeriod = 1; // 默认是1s
}
} else {
purgePeriod = 1; // 默认是1s
}
}
}
1s 的清除周期我觉得有点太频繁了,最终我决定将周期时长改为 60s。最好在首次使用 RxJava 前修改,放到 Application 中最好。