android之handle
异步消息处理机制Handler :动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。 内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。
Handler也是造成内存泄露的一个重要的源头,主要Handler属于TLS(Thread Local Storage)变量,生命周期和Activity是不一致的
,Handler引用Activity会存在内存泄露。
Handler 的生命周期与Activity 不一致
- 当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
- 当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
handler 引用 Activity 阻止了GC对Acivity的回收
- 在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
-
如果外部类是Activity,则会引起Activity泄露 。
当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。如何避免修?
- 使用显形的引用,1.静态内部类。 2. 外部类
- 使用弱引用 2. WeakReference
原文: http://www.cnblogs.com/devinzhang/archive/2011/12/30/2306980.html
知识点总结补充:
很多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,今天就这一问题做更系统性的总结。我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。
对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。
1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒
2. 当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。
3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。
4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 .
5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,这里不做过多的赘述,相关原因参考下面的介绍:
6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务。
Android Handler机制: http://blog.csdn.net/stonecao/article/details/6417364
Handler对Activity finish影响。
在开发的过程中碰到一个棘手的问题,调用Activity.finish函数Acitivity没有执行生命周期的ondestory函数,后面查找半天是因为有一个handler成员,
因为它有一个delay消息没有处理,调用Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory
Android异步消息处理(一): http://www.2cto.com/kf/201109/105866.html
什么是异步消息处理:
对于普通的线程来说,执行完run()方法内的代码后线程就结束了。而异步消息处理线程是指:线程启动后会进入一个无限循环体之中,每执行一次,从线程内部的消息队列中取出一个消息,并回调相应的消息处理函数,执行完一个消息后则继续循环。如果消息队列为空,线程会暂停(一般也就是我们调用休眠方法),直到消息队列中又新的消息。
异步消息处理特点:
从上面的描述可以看出,异步消息处理其实就是一种线程机制,只不过这种机制用的上的地方非常多,最后就单独提炼了“异步消息处理”这个名词。
异步消息处理的使用情况:
一般情况下,如果任务具有以下两个特点,就可以使用异步消息处理机制:
1.任务常驻内存(编程中体现就是run()方法中是无限循环),比如用于处理用户事件的任务。
2.任务需要根据外部传递的消息做不同的操作。
通用的实现异步消息处理机制的方式:
1.每个异步线程内部包含一个消息队列,用来缓存消息。
2.线程的执行中使用while(true)进行无限循环,循环体中从消息队列取出消息,并根据消息的来源,回调相应的消息处理函数(从这里可以看出:异步消息处理,消息的具体处理并不是异步消息处理机制负责的,异步消息处理机制只是负责转发消息给处理函数)
3.其他外部线程可以向本线程的消息队列发送消息,由于有两个或以上的线程访问消息队列,那么,消息队列内部的读写操作必须进行加锁。
Android中异步消息处理的实现方式:
在线程内部有一个或多个Handler对象,外部线程通过该Handler对象的引用向本线程发送异步消息,消息通过Handler对象加入消息队列(MessageQueue)。线程内部只有一个MessageQueue对象,线程的run()方法从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessage()处理消息。
在编程中应该注意的问题:
1.由于异步消息处理机制的一个重要环节是MessageQueue,所以,在发送消息之前,必须确定MessageQueue已经创建。在android中,给应用程序员提供的创建MessageQueue的接口是:Looper.prepare()。
2.由于异步消息处理机制是一个循环线程,而循环的启动也是由程序员控制的,所以,在你要处理消息之前,应该启动循环。在android中,给应用程序员提供的启动循环的接口是:Looper.loop()。
3.细心的朋友可能发现,在activity中使用Handler发送,处理消息时,并没有上面两步,其实是系统帮我们做了的。在创建Activity之前,系统就会为Activity创建一个异步消息处理线程。
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系: http://blog.csdn.net/lmj623565791/article/details/38377229
Android Handler 异步消息处理机制的妙用 创建强大的图片加载类: http://blog.csdn.net/lmj623565791/article/details/38476887