一:概述

前几天app 总是空指针奔溃,发现为某个 Fragment 中的控件为null 导致的。而且这个Fragment 是能够正常显示的。那么为什么还会空指针呢?


二:问题排查

  • 这个app使用了 AndroidAnnotations 注解。要了解为什么 Fragment 中控件为 null 的原因,就需要找到框架 AndroidAnnotationsFragment新建时初始化控件方式,以及在Fragment 销毁时对控件的处理方法。
  • 经排查,发现Fragment 销毁时 AndroidAnnotations 会把所有的控件置为 null
  • 再次进入Fragment 时 ,Fragment 为新建的Fragment 实例,并没有复用原来的Fragment ,里面的控件也正确初始化了。
  • 那么,显然问题不在这里。 app 还采用了 RxJava、RxAndroid,并且 Rx观察者的call 方法里有对Fragment 的控件调用,那么很可能内存泄漏了,Fragment 并没有正确的销毁。Fragment 中的Rx 观察者能正常接收到订阅信息,然后去执行对控件的操作,此时控件已经为null ,那么空指针已经不可避免。

三: 取消 Rxjava、RxAndroid 订阅的方法

一般用过 Rxjava 的人都知道,在ActivityFragment 销毁时,需要取消订阅。取消订阅的方式有多种,如下

  • 用框架管理 RxJava ,如 使用 trello/RxLifecycle开源库或知乎同名开源库zhihu/RxLifecycle,可参考这篇文章,这里不多赘述,网上的文章也挺多的。

  • 不使用开源库,使用类CompositeSubscriptionadd 、unsubscribe等方法管理取消订阅


四: 使用类 unsubscribe 取消订阅

由于引入框架还需要添加依赖,无疑会增加安装包的大小,所以我这里选择使用封装 RxJava本身自带的API去管理取消订阅。

1. 了解 CompositeSubscription
  • CompositeSubscription 用来表示一起取消订阅的一组订阅的订阅。从构造方法可以知道 CompositeSubscription 内部存在一个 HashSet 用来存储需要一起取消订阅的 Subscriptions
  • 调用add方法将Subscription 加入到 Hashset 中。若Hashset 中的 Subscriptions 还没有取消订阅,则把新的 Subscription 加入到 Hashset 中。如果已经取消订阅,则也取消该 Subscription 的订阅
    public void add(final Subscription s) {if (s.isUnsubscribed()) {return;}if (!unsubscribed) {synchronized (this) {if (!unsubscribed) {if (subscriptions == null) {subscriptions = new HashSet<Subscription>(4);}subscriptions.add(s);return;}}}// call after leaving the synchronized block so we're not holding a lock while executing thiss.unsubscribe();}
  • 调用unsubscribe 方法遍历取消 Haset 中的所有订阅,包括它自己。如果此时还有新的Subscription 加入进来,则也取消该Subscription的订阅。
   @Overridepublic void unsubscribe() {if (!unsubscribed) {Collection<Subscription> unsubscribe = null;synchronized (this) {if (unsubscribed) {return;}unsubscribed = true;unsubscribe = subscriptions;subscriptions = null;}// we will only get here onceunsubscribeFromAll(unsubscribe);}}private static void unsubscribeFromAll(Collection<Subscription> subscriptions) {if (subscriptions == null) {return;}List<Throwable> es = null;for (Subscription s : subscriptions) {try {s.unsubscribe();} catch (Throwable e) {if (es == null) {es = new ArrayList<Throwable>();}es.add(e);}}Exceptions.throwIfAny(es);}

2. 最佳实践
  • 定义BaseActivityBaseFragment.具体看代码吧!

BaseActivity

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;import rx.subscriptions.CompositeSubscription;public class BaseActivity extends AppCompatActivity {public  CompositeSubscription compositeSubscription;@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);compositeSubscription = new CompositeSubscription();}@Overrideprotected void onDestroy() {super.onDestroy();//RX生命周期管理if (compositeSubscription != null && !compositeSubscription.isUnsubscribed())compositeSubscription.unsubscribe();}
}

BaseFragment

public class BaseFragment extends Fragment {public CompositeSubscription compositeSubscription;@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);compositeSubscription = new CompositeSubscription();}@Overridepublic void onDestroyView() {super.onDestroyView();//RX生命周期管理if (compositeSubscription != null && !compositeSubscription.isUnsubscribed())compositeSubscription.unsubscribe();}
}
  • 让需要使用RxJavaActivity 继承 BaseActivity ,让需要使用 RxJavaFragment 继承 BaseFragment.
  • 调用 CompositeSubscriptionadd 方法把订阅产生的Subscription 加入到 compositeSubscription中去。
3. 分析:
  • 在订阅产生时,将订阅加入到CompositeSubscription 中。
  • ActivityFragment 销毁时,统一取消所有订阅。

五:总结:

注意:不管是使用框架还是RxJava自带方法,都要在ActivityFragment 销毁时,取消订阅,防止内存泄漏、奔溃等问题。

这样,你才能算会 RxJava、RxAndroid 之取消订阅相关推荐

  1. Android 基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器aaa

    MDPlayer万能播放器 MDPlayer,基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器,可以播 ...

  2. 基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器

    MDPlayer万能播放器 MDPlayer,基于ijkplayer+Rxjava+Rxandroid+Retrofit2.0+MVP+Material Design的android万能播放器,可以播 ...

  3. RxJava/RxAndroid:timer(long delay, TimeUnit unit)

    RxJava/RxAndroid:timer(long delay, TimeUnit unit) timer起到定时器的作用,本例使用timer延迟3秒执行一个输出任务: package com.e ...

  4. RxJava Rxandroid 结合 Retrofit 使用

    其实Retrofit会了.集合RxJava,RxAndroid 就很简单了. 只需要改几个地方. 1.接口里面返回的对象不再是 call,而是Observable public interface A ...

  5. java 安卓下载文件_GitHub - Charay/downloadfile: 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录...

    downloadfile 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录 使用: gradle Step 1.在工程build.gradle ...

  6. 3. fooView rxjava + rxandroid + retrofit 安卓开发框架搭配 az kj

    3. fooView rxjava + rxandroid + retrofit 转载于:https://www.cnblogs.com/cczh/p/9724382.html

  7. 什么样的域名才能算短域名?短域名还能有吗?

    众所周知,域名越短越好,短域名便于用户记忆,同时也更有价值.这里有人要问了,什么样的域名才能算短域名吗?现如今短域名还能有吗?以下是小聚给大家的介绍. 1.什么样的域名才能算短域名? 短域名就是字符短 ...

  8. 姓名学三才数理怎么算?怎样才能算吉凶?姓名打分PHP源码

    一.姓名三才数理的计算原理 三才配置中,三才数理怎么算?怎么样才能算出三才吉凶?三才配置是利用五行生克之理来推断人的吉凶的.因为五行则是对世界万物更为细致的分析,古人认为,世界万物是由木.火.土.金. ...

  9. 【机器学习】用特征量重要度(feature importance)解释模型靠谱么?怎么才能算出更靠谱的重要度?

    [机器学习]用特征量重要度(feature importance)解释模型靠谱么?怎么才能算出更靠谱的重要度? 我们用机器学习解决商业问题的时候,不仅需要训练一个高精度高泛化性的模型,往往还需要解释哪 ...

最新文章

  1. Shiro快速入门 —— 9.freemaker使用shiro标签
  2. js createElement appendChild createTextNode用法
  3. dede服务器建站_建站就是这么简单(内容系统管理CMS篇)
  4. Android中Context简介
  5. 想要导航提示页最新安卓区_最新微信小程序授权的详细处理思路(一)
  6. matlab swt函数,matlab swt 函数出错
  7. python 字节码操作_从操作码和参数列表创建Python字节码?
  8. linux shell 脚本中 字符串截取并赋值引用
  9. 基于matlab的信号频谱分析 开题报告,基于MATLAB的数字信号处理开题报告
  10. 安装Android SDK时无法识别JDK 10
  11. PHP中单引号与双引号用法
  12. 听小S教你如何瘦小腿
  13. LSM6DS3(六轴传感器)STM32驱动及6D功能实现
  14. S32K144(2)时钟配置
  15. 刘慈欣回应《流浪地球》热点问题:承认有些设定有bug...
  16. Inksape 设置画布像素尺寸及透明背景
  17. [免费视频教程]UI自动化测试之Jenkins配置教程
  18. STK与VC++联合编程实战(第三回:加入卫星对象)
  19. 英文学习20180201
  20. 【二十四】Python全栈之路--装饰器

热门文章

  1. win7/10 成功安装sql sevser 2000的方法
  2. 大数据常见面试题之数据仓库
  3. 瘦了10斤,然后拿到了大厂offer
  4. Mycat 读写分离测试
  5. linux的yum安装目录,Linux如何查看YUM的安装目录
  6. 『ML笔记』HOG特征提取原理详解+代码
  7. 03前端学习之CSS3(2)
  8. 英特尔推出它的口袋PC
  9. HR:你该如何调薪加薪?
  10. web页面性能优化方法总结