PS:本文摘抄自《Android高级进阶》,仅供学习使用

  Android代码中涉及线程间通信的地方经常会使用Handler,典型的代码结构如下。

  1. public class HandlerActivity extends Activity{
  2. //可能引入内存泄漏的用法
  3. private final Handler mLeakyHandler = new Handler(){
  4. @Orrvide
  5. public void handleMessage(Mesage msg){
  6. //...
  7. }
  8. };
  9. }

  使用Android Lint分析这段代码,会违反检测项AndroidLintHandlerLeak,得到如下提示。

  1. This Handler class should be static or leaks might occur.

  那么产生内存泄漏的原因可能是什么呢?我们知道,Handler是和Looper以及MessageQueue一起工作的,在Android中,一个应用启动后,系统默认会创建一个为主线程服务的Looper对象,该Looper对象用于处理主线程的所有Message对象,它的生命周期贯穿于整个应用的生命周期。在主线程中使用的Handler都会默认绑定到这个Looper对象。在主线程中创建Handler对象,它会立即关联到主线程Looper对象的MessageQueue,这时发送到MessageQueue中的Message对象都会只有这个Handler对象的引用,这样在Looper处理消息时常能回调到Handler的handlerMessage方法。因此,如果Message还没有被处理完成,那么Handler对象也就不会被垃圾回收。

  在上面的代码中,将Handler的实例声明为HandlerActivity类的内部类。而在Java语言中,非静态内部匿名类会持有外部类的一个隐式的引用,这样就可能会导致外部类无法被垃圾回收。因此,最终由于MessageQueue中Message还没处理完成,就会持有Handler对象的引用,而非静态的Handler对象会持有外部类HandlerActivity的引用,这个Activity无法被垃圾回收,从而导致内存泄漏。

  一个明显的会引入内存泄漏的例子如下。

  1. public class HandlerActivity extends Activity{
  2. //可能引入内存泄漏的用法
  3. private final Handler mLeakyHandler = new Handler(){
  4. @Orrivide
  5. public void handleMessage(Mesage msg){
  6. //...
  7. }
  8. };
  9. @Orrivide
  10. protected void onCreate(Bundle savedInstanceState){
  11. super.onCreate(savedInstanceState);
  12. //延迟5分钟发送消息
  13. mLeakyHandler.postDelayed(new Runnable(){
  14. @Orrivide
  15. public void run(){/* ... */}
  16. }, 1000*60*5);
  17. }
  18. }

  由于消息延长5分钟,因此,当用户进入这个Activity并退出后,在消息发送并处理完成之前,这个Activity是不会被系统回收的(系统内存确实不够使用的情况例外)。

  如果解决呢。有两个方案。

  • 在子线程中使用Handler,这时需要开发者自己创建一个Looper对象,这个Looper对象的生命周期同一般的Java对象,因此这种用法没有问题。
  • 将Handler生命为静态的内部类,前面说过,静态内部类不会持有外部类的引用,英寸,野不会引起内存泄漏,经典用法的代码如下。
  1. public class HandlerAcitivity extends Activity{
  2. //声明一个静态的Handler内部类,并持有外部类的弱引用
  3. private static class InnerHandler extends Handler{
  4. private final WeakReference<HandlerAcitivity> mActivity;
  5. public InnerHandler(HandlerAcitivity activity){
  6. mActivity = new WeakReference<HandlerAcitivity>(activity);
  7. }
  8. @Override
  9. public void handleMessage(Message msg){
  10. HandlerAcitivity activity = mActivity.get();
  11. if(activity!=null){
  12. //...
  13. }
  14. }
  15. }
  16. private final InnerHandler mHandler = new InnerHandler(this);
  17. //静态的匿名内部类不会持有外部类的引用
  18. private static final Runnable sRunnable = new Runnable(){
  19. @Override
  20. public void run(){
  21. //...
  22. }
  23. };
  24. @Override
  25. protected void onCreate(Bundle savedInstanceState){
  26. super.onCreate(savedInstanceState);
  27. //延迟5分钟发送消息
  28. mHandler.postDelay(sRunnable, 1000*60*5);
  29. }
  30. }

  

  

Handler和内部类的正确用法的更多相关文章

  1. 对象及变量的并发访问(同步方法、同步代码块、对class进行加锁、线程死锁)&内部类的基本用法

    主要学习多线程的并发访问,也就是使得线程安全. 同步的单词为synchronized,异步的单词为asynchronized 同步主要就是通过锁的方式实现,一种就是隐式锁,另一种是显示锁Lock,本节 ...

  2. Spring MVC中Session的正确用法<转>

    Spring MVC是个非常优秀的框架,其优秀之处继承自Spring本身依赖注入(Dependency Injection)的强大的模块化和可配置性,其设计处处透露着易用性.可复用性与易集成性.优良的 ...

  3. C#中dynamic的正确用法

    C#中dynamic的正确用法  http://www.cnblogs.com/qiuweiguo/archive/2011/08/03/2125982.html dynamic是FrameWork4 ...

  4. C# string.Split对于换行符的分隔正确用法

    C# string.Split对于换行符的分隔正确用法 tmpCase "11117144-8c91-4817-9b92-99ec2f9d784a\r\n23D95A26-012C-4332 ...

  5. 【转】Spring MVC中Session的正确用法之我见

    Spring MVC是个非常优秀的框架,其优秀之处继承自Spring本身依赖注入(Dependency Injection)的强大的模块化和可配置性,其设计处处透露着易用性.可复用性与易集成性.优良的 ...

  6. 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

    Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解   多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...

  7. C#中dynamic的正确用法 以及 typeof(DynamicSample).GetMethod("Add");

    dynamic是FrameWork4.0的新特性.dynamic的出现让C#具有了弱语言类型的特性.编译器在编译的时候不再对类型进行检查,编译期默认dynamic对象支持你想要的任何特性.比如,即使你 ...

  8. 【转】改善C#程序的建议2:C#中dynamic的正确用法 空间

    dynamic是FrameWork4.0的新特性.dynamic的出现让C#具有了弱语言类型的特性.编译器在编译的时候不再对类型进行检查,编译期默认dynamic对象支持你想要的任何特性.比如,即使你 ...

  9. C#中dynamic、ExpandoObject 的正确用法

    原文地址:http://www.cnblogs.com/qiuweiguo/archive/2011/08/03/2125982.html dynamic是FrameWork4.0的新特性.dynam ...

随机推荐

  1. idea清除缓存和索引

    转自:https://blog.csdn.net/mzy755423868/article/details/80559381

  2. Ordeby then by

    先按orderby排序,再按thenby排序 return PartialView("_ClickRangeOnCategory", articles.OrderByDescend ...

  3. Mybatis invalid comparison: java.util.Date and java.lang.String

    Mybatis的实体类是java.utils.Date类型,而在Mybatis的XML文件中,使用if判断了,不需要判断是否等于空字符串这种判断,需要人真一些 相关:https://blog.csdn ...

  4. Math对象常用方法(取整细节)

    Math 对象 Math 对象用于执行数学任务. 1.常用属性: 1.E :返回算术常量e,即自然对数的底数(约2.718) 2.PI :返回圆周率,约3.14159 2.常用方法    Math.方 ...

  5. bzoj 4080: [Wf2014]Sensor Network【瞎搞+随机化】

    参考:https://blog.csdn.net/YihAN_Z/article/details/73380387 一点都不想写正解.jpg random_shuffle一下然后贪心的加点,和ans取 ...

  6. bzoj 2809: [Apio2012]dispatching【dfs序+主席树】

    可并堆就可以,但是想复健一下主席树. 考虑枚举管理者,然后选忍者的时候在子树中贪心的从小到大选.做成dfs序就是选区间内和小于等于k的最多点.可以用主席树,查询的时候在主席树上二分即可 这里注意,为了 ...

  7. P5068 [Ynoi2015]我回来了

    传送门 解锁成就:ynoi的题目都做到过原题 因为\(n\)很小,我们可以用\(sss[u][i]\)表示到点\(u\)的距离不超过\(i\)的点的集合,这个可以用bitset存,然后先一遍bfs,再 ...

  8. Akka源码分析-Serialization

    今天我们来谈一下akka的序列化框架,其实序列化.反序列化是一个老生常谈的问题,那么我们为什么还要研究一下akka的序列化框架呢?不就是使用哪种序列化.反序列化方法的区别么?其实刚开始的时候我也是这么 ...

  9. mac下安装ngnix以及开启关闭重启

    一.安装 执行如下命令 brew search nginx brew install nginx 安装完以后,可以在终端输出的信息里看到一些配置路径: /usr/local/etc/nginx/ngi ...

  10. 【SpringCloud构建微服务系列】微服务网关Zuul

    一.为什么要用微服务网关 在微服务架构中,一般不同的微服务有不同的网络地址,而外部客户端(如手机APP)可能需要调用多个接口才能完成一次业务需求.例如一个电影购票的手机APP,可能会调用多个微服务的接 ...