在任何程序开发中,异步操作的处理都是一个麻烦事,而在 Android 中更繁杂一些,这是由于 Android 基于组件的设计对异步操作不够友好。所以,如果你在 Android 中开发界面,不妥善处理全部的异步回调,崩溃、内存泄露、状态错乱,就都接踵而至了。

而在 Android 中如何处理好异步请求,则是一个非常宽泛的话题,从这篇开始的若干篇,都会围绕这个来聊一聊。而这篇要讲的,就是看看界面中的异步回调,经常会引发什么问题,要注意些什么。

要在异步回调中判断界面状态

最典型的问题,就是回调时触碰界面而引发崩溃,一个简单示例如下:

// 发起一个异步请求,执行完成后更新界面状态
requestManager.execute(aRequest, new Callback() {protected void completed() {textView.setText("Completed!");}
});

如果回调时,用户已经离开了界面,textView 可能会由于状态不对抛出异常而引发程序崩溃。因此,只要在页面上做了异步回调,需仔细考虑回调时的页面状态,可能需要调用 Activity.isFinishing 来检查界面是否销毁避免触发状态异常的崩溃;如果在 Fragment 中,就要调用 Fragment.isAdded 进行判断,避免 Activity 解绑后触发空指针;以及,在调用各种 Fragment 的 APIs 时,都需要确定页面是否 Save State 了,否则很多 Fragment 的调用都会出错。当然,简单粗暴的,还能赤果果把回调操作中全部 Runtime 级别开始的全部异常都吃掉,不优雅,但至少比崩溃好...

但其实,解决回调崩溃的问题是知易行难,真实的项目中,情况会非常隐匿,线上崩溃总是由于 "不小心"、"没想到" 而引发的。所以,很多时候把保护放在服务内而不是调用者那里,是更安全可靠的方案。

要尽可能的取消异步操作

除了崩溃,和异步形影不离的还有内存泄露,它的隐蔽性很强,总是藏匿在 Activity 等具有 Context 的对象中。比如:

// 执行一个时间很长的操作...
requestManager.execute(aRequest(activity) {protected void run() {// wait long long time...}
});

这个异步操作拿了 Activity 对象,如果它需要执行很长时间,那在这个阶段 Activity 和它上面的各种元素都是无法释放的,在用户离开页面很长时间后,内存依旧在占用,并且反复进出会持续累加直到 Out Of Memory(OOM),这其实也就是引发了内存泄露。而这样长时间的等待,有可能是定时引发、有可能是队列排队引发、有可能卡在了某个操作上(比如网络、IO ...),很多时候,甚至都难于提前判断。

因此,如果我们假设,大部分的异步操作都可能是慢的,需要执行很长时间,那如何来处理呢?使用弱引用是一个办法,把等待回调的界面对象(一般都持有 Context)用 WeakReference 包裹起来,如果用户离开页面,很快也会把它给收了,以此来避免内存泄露。但弱引用本身使用上有很多禁忌,稍有不慎就从一个坑到了另一个坑。比如,弱引用最好不要放到服务内弄成黑盒,而是要调用者自行使用,如果放在服务内调用者很可能会出现傻等回调等不来的状况;而一旦完全交由调用者,又难以保证实现是统一可靠的,依旧会由于 "不小心" 引发问题。

除此之外呢?支持干净的取消可以算是最漂亮的方案之一了,在异步回调未返回但界面要退出之前,把操作给回收了,而这个回收不只是设定一个标志位,而是要将回调中潜在泄露的对象彻底移除,这样才能是干净的、可以避免内存泄露的取消方案。

使用LeakCanary检测内存泄漏

        内存泄漏可能是一个难以检测和解决的难题,小的内存泄漏隐藏的比较深,并且在应用程序长时间使用后可能会出现,所以寻找内存泄漏并不是一项简单的任务。在本教程中,我们将创建一个泄漏的应用程序,我们将使用LeakCanary库来检测内存泄漏。
第1步:将LeakCanary依赖项添加到应用程序   
dependencies {debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'}
第2步:扩展和配置应用程序类
        我们需要在onCreate方法中调用LeakCanary.install:
import android.content.Context;import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;public class MyApplicationextends Application {private RefWatcher refWatcher;public static RefWatcher getRefWatcher(Context context) {MyApplication application = (MyApplication) context.getApplicationContext();return application.refWatcher;}@Overridepublic void onCreate() {super.onCreate();refWatcher = LeakCanary.install(this);}}
第3步:创建一个泄漏的活动
        为此,我们将创建一个保存上下文的单例类:
import android.content.Context;public class SingletonSavesContext {private Context context;private static SingletonSavesContext instance;public Context getContext() {return context;}public void setContext(Contextcontext) {this.context = context;}publicstatic SingletonSavesContextgetInstance() {if (instance == null) {instance = new SingletonSavesContext();}return instance;}}
        然后,在主Acitivty中,添加如下代码测试:
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;import com.squareup.leakcanary.RefWatcher;
import com.youshixiu.kuplaywawa.R;public class LeakedActivity extends AppCompatActivity {@Overrideprotected void onCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new MyAsyncTask().execute(this);}@Overridepublic void onDestroy() {super.onDestroy();RefWatcher refWatcher = MyApplication.getRefWatcher(this);refWatcher.watch(this);}public class MyAsyncTask extends AsyncTask<Object, String, String> {private Context context;@Overrideprotected String doInBackground(Object... params) {context = (Context) params[0];
// Invoke the leak!SingletonSavesContext.getInstance().setContext(context);
// Simulate long running tasktry {Thread.sleep(3000);} catch (InterruptedException e) {}return "result";}@Overrideprotected void onPostExecute(Strings) {super.onPostExecute(s);Intent newActivity = new Intent(context, AnotherActivity.class);startActivity(newActivity);}}}

然后,在新的活动中,我们将调用System.gc强制垃圾收集器来加速分析。

第4步:检索分析结果
        通知栏会显示泄漏信息:
        结果也可以从logcat中获取:

14211-16753/anaware.leakcanarysample D/LeakCanary: In anaware.leakcanarysample:1.0:1.14211-16753/anaware.leakcanarysample D/LeakCanary: * anaware.leakcanarysample.LeakedActivity has leaked:14211-16753/anaware.leakcanarysample D/LeakCanary: * GC ROOT static anaware.leakcanarysample.SingletonSavesContext.instance14211-16753/anaware.leakcanarysample D/LeakCanary: * references anaware.leakcanarysample.SingletonSavesContext.context14211-16753/anaware.leakcanarysample D/LeakCanary: * leaks anaware.leakcanarysample.LeakedActivity instance14211-16753/anaware.leakcanarysample D/LeakCanary: * Reference Key: 226ffd5e-902c-4202-8508-2e268616e2ae14211-16753/anaware.leakcanarysample D/LeakCanary: * Device: samsung samsung GT-I9305 m3xx14211-16753/anaware.leakcanarysample D/LeakCanary: * Android Version: 4.4.4 API: 19 LeakCanary: 1.3.114211-16753/anaware.leakcanarysample D/LeakCanary: * Durations: watch=5007ms, gc=120ms, heap dump=617ms, analysis=13070ms14211-16753/anaware.leakcanarysample D/LeakCanary: * Details:14211-16753/anaware.leakcanarysample D/LeakCanary: * Class anaware.leakcanarysample.SingletonSavesContext14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40]14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8]14211-16753/anaware.leakcanarysample D/LeakCanary: * Instance of anaware.leakcanarysample.SingletonSavesContext14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40]14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8]14211-16753/anaware.leakcanarysample D/LeakCanary: | context = anaware.leakcanarysample.Leake

内存泄漏,关于异步回调导致的内存泄漏,使用LeakCanary检测内存泄漏相关推荐

  1. Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    本篇文章主要介绍了Android性能优化之利用LeakCanary检测内存泄漏及解决办法,有兴趣的同学可以了解一下. 目录 前言 什么是内存泄漏? 内存泄漏造成什么影响? 什么是LeakCanary? ...

  2. 使用LeakCanary检测内存泄露

    前言 刚才在项目里使用LeakCanary检测出了一个使用NotificationBuilder导致的内存泄露,发现LeakCanary真是神器啊.这里转载一篇介绍LeakCanary使用的博客,里面 ...

  3. leakCanary检测内存泄漏的原理

    LeakCanary是Square公司为Android开发者提供的一个自动检测内存泄漏的工具,LeakCanary本质上是一个基于MAT进行Android应用程序内存泄漏自动化检测的的开源工具,我们可 ...

  4. 为什么使用LeakCanary检测内存泄漏?

    文章目录 为什么要使用LeakCanary? LeakCanary是怎么工作的? 源码分析 初始化 buildAndInstall()方法中的操作 1.RefWatcher类 2.DisplayLea ...

  5. leaks Android内存泄露,Android LeakCanary 检测内存泄露

    内存泄漏: 指程序在申请内存后 ,无法释放已经申请的内存空间,一次内存泄漏可以忽略,但内存泄漏堆积后果很严重,无论多少内存,都会被占光 内存泄露危害: 1.内存泄露最终会导致内存溢出(OOM) 2.导 ...

  6. VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏[转]

    /C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:"最大的长处也可能成为最大的弱点",那么 C/C++ 应用程序正好印证了这句话.在 C/C++ 应用程 ...

  7. 通过DT10检测内存泄漏问题

    DT10是新一代的动态测试工具,可以长时间跟踪记录目标程序执行情况,获取目标程序动态执行数据,帮助进行难于重现的Bug错误分析,覆盖率检测,性能测试,变量跟踪等等功能.   C/C++代码中,内存泄漏 ...

  8. iPhone Instruments工具使用_检测内存泄露(转)

    最近常使用Instruments这个工具,我发现它对追踪游戏中的内存泄露非常有帮助.自从发现Instruments如此有用后,我就觉得写一篇文章介绍如何使用它来追踪内存泄露对其他人也会有帮助. 什么是 ...

  9. 计算机test的应用,memtest怎么用,教您如何使用MemTest检测内存

    生活中,电脑已经越来越重要,电脑的健康也决定着你的生活与工作的效率.在日常使用电脑时,打开一些软件或者游戏时,电脑有时发生蓝屏或卡机状态,这很可能是内存发生了故障,该怎么检测内存呢?下面,小编给大家介 ...

最新文章

  1. EEGLAB处理脑电视频教程 part1-3
  2. 马云狂炸近百亿,你的借呗额度涨了吗?
  3. Mybatis常用总结:参数,返回,执行sql,include等
  4. Mybatisplus用updateById默认没有传的值不会进行改变
  5. IOC容器(底层原理解读)
  6. linux下的C语言开发(ATT 汇编语言)
  7. c语言fopen函数读dat文件,C语言 从a.dat文件读入数据存入b.dat文件里面(以字符形式)...
  8. Android 系统开发系列四
  9. Linux用户的福音,记忆力解放!快速调用复杂命令...
  10. Django项目将debug模式设置为false时,静态文件出错
  11. 对象创建从农业社会到共产主义的发展
  12. 微信小程序2:网易云音乐(完整版)
  13. 暴风影音2007全功能完美版和Symantec Norton的冲突
  14. Rainmeter雨滴天气-(永不过时版通过获取网页数据实现)
  15. 软件测试周刊(第46期):走好选择的路,别选择好走的路,你才能拥有真正的自己。
  16. Saver类--变量的保存和恢复
  17. 实现点击图标使界面回到顶部
  18. Kotlin读书笔记之函数式kotlin
  19. Java 提取PDF图片(pdfbox)Extract PDF document images
  20. 1134: 小青蛙,跳跳跳

热门文章

  1. [Python3网络爬虫开发实战] -爬取电影排行数据
  2. Linux的优缺点,Linux与windows的区别
  3. mysql会自动建立索引_mysql innodb 索引是自动建立的吗
  4. IntelliJ IDEA:activation code 激活码(Windows)
  5. 操作系统 - C语言实现生产者消费者问题
  6. 常见算法之典型数组处理(java)
  7. 若依-用户长时间未修改密码强制修改
  8. 解决pip install -r requirements.txt安装过程中出现的问题
  9. 一文get关于BSCI的新知识
  10. 非常详细的VirtualBox安装Ubuntu虚拟机教程【新建虚拟电脑+Ubuntu系统安装过程+VirtualBox安装增强功能工具VBoxGuestAdditions】