转自:Android中使用Handler引发的内存泄露

在Activity中,经常会用到自定义的Handler来处理主线程收到的Message,但是ADT20以后,直接定义的如下定义的内部会有提示说这种使用方法有内存泄漏的风险:

private Handler mHandle = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
// this is the code
break;
default:
break;
}
super.handleMessage(msg);
}
};

当使用Android lint工具的话,会得到这样的警告,具体的提示如下:

This Handler class should be static or leaks might occur (com.example.multifragment.SampleActivity.1) Issue: Ensures that Handler classes do not hold on to a reference to an outer class Id: HandlerLeak 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 Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

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); // Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10); // Go back to the previous Activity.
finish();
}
}

分析一下上面的代码,当我们执行了Activity的finish方法,被延迟的消息会在被处理之前存在于主线程消息队列中10分钟,而这个消息中又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的SampleActivity的引用,所以这导致了SampleActivity无法回收,进行导致SampleActivity持有的很多资源都无法回收,这就是我们常说的内存泄露。

注意上面的new Runnable这里也是匿名内部类实现的,同样也会持有SampleActivity的引用,也会阻止SampleActivity被回收。

要解决这种问题,思路就是不适用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用。 修改后不会导致内存泄露的代码如下

public class SampleActivity extends Activity {

  /**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<sampleactivity> mActivity; 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); /**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are static.
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity.
finish();
}
}

其实在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,就像本文提到的一样,所以当我们使用时要非静态内部类时要格外注意,如果其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。倾向于使用文章的静态类和弱引用的方法解决这种问题。

除了上述弱引用的方法,其他解决方法:@SuppressLint("HandlerLeak")应用问题

第一种:(不推荐)根据提示,直接加入“@SuppressLint("HandlerLeak")”的注释或者在Lint Error Checking里面检索HandlerLeak,然后选择ignore。不推荐,这种方法实际上没有解决问题。

第二种 :把Handler定义成static,然后用post方法把Runnable对象传送到主线程,这种方法将要发送的消息转换成了对应的Runable对象,适用于只有一个消息要发送的情形,如果有多个消息要发送可以采用上述弱引用的方法(handler要覆盖handleMessage处理各种收到的消息的时候)。

public class SettingsActivity extends Activity {
private static Handler handler;
private TextView textView; public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create a handler to update the UI
handler = new Handler();
// ... // handler.post(new Runnable() {} handler.post(new Runnable() {
@Override
public void run() {
// 此处放入要改变UI的操作即可
textView.setText("success"); // 改变textView显示的内容
}
});
} }

Android Handler Leak的更多相关文章

  1. Android Handler leak 分析及解决办法

    In Android, Handler classes should be static or leaks might occur, Messages enqueued on the applicat ...

  2. Android Handler练习

    package com.example.myact12; import java.util.Random; import android.support.v7.app.ActionBarActivit ...

  3. Android Handler简单使用

    package com.example.myhandlertest3; import android.os.Bundle; import android.os.Handler; import andr ...

  4. Android Handler的使用

    大家好我们这一节讲的是Android Handler的使用,在讲Handler之前,我们先提个小问题,就是如何让程序5秒钟更新一下Title. 首先我们看一下习惯了Java编程的人,在不知道Handl ...

  5. [Android]Handler的消息机制

    最经面试中,技术面试中有一个是Handler的消息机制,细细想想,我经常用到的Handler无非是在主线程(或者说Activity)新建一个Handler对象,另外一个Thread是异步加载数据,同时 ...

  6. 详解Android Handler的使用-别说你不懂handler

    我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主 ...

  7. Android handler Thread 修改UI Demo

    /********************************************************************** * Android handler Thread 修改U ...

  8. Android Handler的简单使用

    大家好我们这一节讲的是Android Handler的使用,在讲Handler之前,我们先提个小问题,就是如何让程序5秒钟更新一下Title. 首先我们看一下习惯了Java编程的人,在不知道Handl ...

  9. 详解Android Handler的使用

    我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主 ...

随机推荐

  1. poj 2975 Nim_最经典的Nim取石子

    题意:给你n堆石头,每次只能在一堆取最少一个石子,最后拿走最后一堆的为胜者,问胜者有多少种赢得取法 #include <iostream> #include<cstdio> u ...

  2. ubuntu 下添加sublime text 启动图标

    ubuntu下无快捷方式一说,如果想快速打开sublime Text,可以创建sublime text 启动图标 1. 创建空白文档 sublime.desktop,注意权限 $ vi /usr/sh ...

  3. #include <set>

    1 multiset 多重集合(multiset) 允许存在两个次序相等的元素的集合 <set> 2 set 集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作 ...

  4. BZOJ 3181([Coci2012]BROJ-最小质因子为p的第k小素数)

    3181: [Coci2012]BROJ Time Limit: 10 Sec   Memory Limit: 64 MB Submit: 26   Solved: 7 [ Submit][ Stat ...

  5. 泛泰A900 刷4.4专用中文TWRP2.7.1.1版 支持自己主动识别手机版本号(全球首创)

    因本人手上的A900S已砖, 所以临时弄不了ROM了, 先上传之前已经弄好的刷4.4专用的新版TWRP recovery 2.7.1.1  这个版本号是我自己定义的,为差别之前公布的2.7.0.0版( ...

  6. &lt;转&gt;凯文&#183;凯利斯坦福演讲-预言未来20年科技潮流

    Note:未来全部的生意都是关于数据的生意,近场通信.自组网介入网络.人工智能...,如今的IT科技界是否仅仅是冰山一角.斑斓舞台帷幕的一丝缝隙? 原文出处: 中欧管理工商学院   欢迎分享原创到伯乐 ...

  7. Zepto 使用中的一些注意点

    Zepto 只针对移动端浏览器编写,因此体积更小.效率更高,更重要的是,它的 API 完全仿照 jQuery ,所以学习成本也很低. 但是在开发过程中,我发现 Zepto 还远未成熟,其中包含了一些或 ...

  8. ORACLE表空间bigfile和smallfile

    BIGFILE | SMALLFILE Use this clause to determine whether the tablespace is a bigfile or smallfile ta ...

  9. [Tree]Binary Tree Inorder Traversal

    Total Accepted: 98729 Total Submissions: 261539 Difficulty: Medium Given a binary tree, return the i ...

  10. C++_String

    String -String的实现 -String常用操作