安卓handler发送消息过多会handler造成内存泄露露吗

posts - 164,&
comments - 9,&
trackbacks - 0
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。
中使用Handler造成内存泄露的原因
Handler mHandler = new Handler() {& & @Override& & public void handleMessage(Message msg) {& & & & mImageView.setImageBitmap(mBitmap);& & }}
上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -& Message -& Handler -& Activity的链,导致你的Activity被持有引用而无法被回收。
内存泄露的危害
只有一个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC。
使用Handler导致内存泄露的解决方法
方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
方法二:将Handler声明为静态类。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:
static class MyHandler extends Handler {& & @Override& & public void handleMessage(Message msg) {& & & & mImageView.setImageBitmap(mBitmap);& & }}
但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):
static class MyHandler extends Handler {& & WeakReference&Activity & mActivityR
& & MyHandler(Activity activity) {& & & & mActivityReference= new WeakReference&Activity&(activity);& & }
& & @Override& & public void handleMessage(Message msg) {& & & & final Activity activity = mActivityReference.get();& & & & if (activity != null) {& & & & & & mImageView.setImageBitmap(mBitmap);& & & & }& & }}
将代码改为以上形式之后,就算完成了。
延伸:什么是WeakReference?
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。
阅读(...) 评论()中国领先的IT技术网站
51CTO旗下网站
Android 中 Handler 引起的内存泄露
在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用。其实这可能导致内存泄露,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下。
作者:佚名来源:droidyue| 13:50
在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用。通常我们的代码会这样实现。
public&class&SampleActivity&extends&Activity&{&&&&private&final&Handler&mLeakyHandler&=&new&Handler()&{&&&&&@Override&&&&&public&void&handleMessage(Message&msg)&{&&&&&&&&&&&&}&&&}&}&
但是,其实上面的代码可能导致内存泄露,当你使用Android lint工具的话,会得到这样的警告
In Android, Handler classes should be static or leaks
might occur, Messages enqueued on the application thread&s MessageQueue
also retain their target Handler. If the Handler is an inner class, its
outer class will be retained as well. To avoid leaking the outer class,
declare the Handler as a static nested class with a WeakReference to its
outer class
看到这里,可能还是有一些搞不清楚,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下。
1.当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中 的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到 Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长。
2.当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上 已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用 Handler#handleMessage(Message)完成消息的正确处理。
3.在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。关于这一内容可以查看
确实上面的代码示例有点难以察觉内存泄露,那么下面的例子就非常明显了
public&class&SampleActivity&extends&Activity&{&&&&private&final&Handler&mLeakyHandler&=&new&Handler()&{&&&&&@Override&&&&&public&void&handleMessage(Message&msg)&{&&&&&&&&&&&&}&&&}&&&&@Override&&&protected&void&onCreate(Bundle&savedInstanceState)&{&&&&&super.onCreate(savedInstanceState);&&&&&&&&&&&mLeakyHandler.postDelayed(new&Runnable()&{&&&&&&&@Override&&&&&&&public&void&run()&{&&}&&&&&},&1000&*&60&*&10);&&&&&&&&&&&finish();&&&}&}&
分析一下上面的代码,当我们执行了Activity的finish方法,被延迟的消息会在被处理之前存在于主线程消息队列中10分钟,而这个消息中 又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的SampleActivity的引用,所以这导致了 SampleActivity无法回收,进行导致SampleActivity持有的很多资源都无法回收,这就是我们常说的内存泄露。
注意上面的new Runnable这里也是匿名内部类实现的,同样也会持有SampleActivity的引用,也会阻止SampleActivity被回收。
要解决这种问题,思路就是不适用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用。 修改后不会导致内存泄露的代码如下
public&class&SampleActivity&extends&Activity&{&&&&&&&&&&private&static&class&MyHandler&extends&Handler&{&&&&&private&final&WeakReference&SampleActivity&&mA&&&&&&public&MyHandler(SampleActivity&activity)&{&&&&&&&mActivity&=&new&WeakReference&SampleActivity&(activity);&&&&&}&&&&&&@Override&&&&&public&void&handleMessage(Message&msg)&{&&&&&&&SampleActivity&activity&=&mActivity.get();&&&&&&&if&(activity&!=&null)&{&&&&&&&&&&&&&&&&}&&&&&}&&&}&&&&private&final&MyHandler&mHandler&=&new&MyHandler(this);&&&&&&&&&&private&static&final&Runnable&sRunnable&=&new&Runnable()&{&&&&&&&@Override&&&&&&&public&void&run()&{&&}&&&};&&&&@Override&&&protected&void&onCreate(Bundle&savedInstanceState)&{&&&&&super.onCreate(savedInstanceState);&&&&&&&&&&&mHandler.postDelayed(sRunnable,&1000&*&60&*&10);&&&&&&&&&&&finish();&&&}&}&
其实在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,就像本文提到的一样,所以当我们使用时要非静态内部 类时要格外注意,如果其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。个人倾向于使用文章的静态类和弱引用的方法解决这种问题。
【编辑推荐】【责任编辑: TEL:(010)】
大家都在看猜你喜欢
头条外电头条原创原创
24H热文一周话题本月最赞
讲师:21人学习过
讲师:7人学习过
讲师:12人学习过
精选博文论坛热帖下载排行
主要内容:
● 如何设计像自动售货机那样有效的用户界面。
● 深入理解窗口和对话框的管理机制。
● 为什么性能优化与我们在直觉上的理...
订阅51CTO邮刊Android handler 可能会造成内存泄露
Android Studio
使用 Handler 时;
private Handler handler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
// handle something
Android Studio 弹出了警告;
This Handler class should be static or leaks might occur (null) less... (Ctrl+F1)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handl In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the H Make all references to members of the outer class using the WeakReference object.
如果这个Handler用的是主线程外的Looper或者消息队列就没事。若是在主线程中,Handler的消息还在队列中尚未被处理,GC有可能无法回收这个Handler;也就无法回收使用这个Handler的外部类(如Activity、Service),有可能引起OOM。
换句话说,当Activity结束时,延迟的消息还可能在消息队列中。消息引用这Activity的Handler,Handler持有着它的外部类(也就是这个Activity)。这个引用会持续到消息被处理,在此之前不会被GC。
Java中,非静态内部类和匿名类会引用了它们的外部类。
根据修改建议,按照提示进行修改,将handler声明为static。
static class rustHandler extends Handler {
private final WeakReference&MySwipeActivity& mA
rustHandler(MySwipeActivity mySwipeActivity) {
mActivity = new WeakReference&MySwipeActivity&(mySwipeActivity);
public void handleMessage(Message msg) {
MySwipeActivity activity = mActivity.get();
if (activity != null) {
// la la la la la ...
静态类 rustHandler 中定义一个 WeakReference(弱引用);利用这个若引用来完成操作。这样警告就消失了。
参考Android源码,也有这种做法,没有使用弱引用的
static class MyHandler extends Handler {
MySwipeActivity handlerSwipeA
MyHandler(MySwipeActivity mySwipeActivity) {
handlerSwipeActivity = mySwipeA
public void handleMessage(Message msg) {
if (handlerSwipeActivity != null) {
// do something
同样消除了AS的警告;MyHandler 声明为 static,达到了目的
Java中的引用
java.lang.ref 类库包含了一组类,这些类为垃圾回收提供了更大的灵活性。
SoftReference WeakReference PhantomReference 由强到弱,对应不同级别的“可获得性”;越弱表示对垃圾回收器的限制越少,对象越容易被回收。
阅读(...) 评论()5806人阅读
Android性能优化(12)
关于Android中使用Handler造成内存泄露的分析和解决什么是内存泄露?Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。Android中使用Handler造成内存泄露的原因Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}如下图所示:编译器便已提示:该程序类应该是静态的且可能会发生泄漏上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -& Message -& Handler -& Activity 的链,导致你的Activity被持有引用而无法被回收。内存泄露的危害只有一个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC。使用Handler导致内存泄露的解决方法方法一:通过程序逻辑来进行保护。1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。方法二:将Handler声明为静态类。静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:static class MyHandler extends Handler {
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):static class MyHandler extends Handler {
WeakReference&Activity & mActivityR
MyHandler(Activity activity) {
mActivityReference= new WeakReference&Activity&(activity);
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}将代码改为以上形式之后,就算完成了。延伸:什么是WeakReference?WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。转自:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1361861次
积分:14912
积分:14912
排名:第740名
原创:216篇
评论:580条
欢迎加入讨论群,相互学习,共同进步
文章:23篇
阅读:111190
阅读:57367
阅读:43425
文章:40篇
阅读:249033
文章:17篇
阅读:95186
文章:30篇
阅读:178052
(4)(2)(1)(1)(1)(3)(1)(4)(3)(14)(13)(9)(9)(18)(19)(6)(9)(15)(14)(7)(23)(3)(26)(12)(1)(3)}

我要回帖

更多关于 handler取消发送消息 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信