Android 内存泄漏之handler
来源:互联网 发布:显示登录成功和链接php 编辑:程序博客网 时间:2024/06/05 20:39
问题引出
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myHandler.sendEmptyMessageDelayed(1,60000); } Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: break; } } };}
这是我们通常使用handler的写法。
然而IDE一般会警告:
This Handler class should be static or leaks might occur (anonymous android.os.Handler) 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 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. Handler myHandler = new Handler(){
也就是Handler myHandler = new Handler()这句话存在问题。假设Handler刚刚sendEmptyMessageDelayed,立马关闭Activity,那么就会造成内存泄漏
问题原因
Handler 的生命周期与Activity 不一致
当Android应用启动的时候,会先创建一个UI主线程的Looper对象(即Looper对象的生命周期与Application同步),Looper实现了一个简单的消息队列(MessageQueue),一个一个的处理里面的Message对象(使用死循环遍历处理Message)。主线程Looper对象在整个应用生命周期中存在。
当当前Activity(比如上面的mainActivity)在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
此时就形成了一个关系链:
Application–>Looper–>MessageQueue–>Message–>Handler–>mainActivity
Activity销毁时,想要把mainActivity GC 掉,但是由于Message还没有处理,导致handler仍然保持着mainActivity的引用,handler 引用 Activity 阻止了GC对Acivity的回收
当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
补充一:内存泄漏的原因:
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。
补充二:关于内部类:
在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
如果外部类是Activity,则会引起Activity泄露 。
解决方案:
- 方法一:通过程序逻辑来进行保护。
1.总的来说(Activity关闭的时候可能存在其他形式的线程仍然引用着Activity)在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了线程r和外部连接的线,Activity自然会在合适的时候被回收。
2.针对Handler的GC如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
代码实现 在Activity销毁时把消息对象从消息队列移除,解除引用。
public void onDestroy() { // If null, all callbacks and messages will be removed. mHandler.removeCallbacksAndMessages(null);}
使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。
以上属于代码维护,虽然解决了问题,但是警告依然存在。有代码洁癖的请看第二种方法。
方法二:将Handler声明为静态类。
分二步
1 静态类不持有外部类的对象,所以你的Activity可以随意被回收。改成静态类
2 使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference),另外非静态方法todo不能在静态类调用
第一步: 改静态
public class MainActivity extends AppCompatActivity { MyHandler myHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myHandler.sendEmptyMessageDelayed(1, 60000); } public void todo() { } static class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); todo(); } }}
第二步: 添加弱引用
import java.lang.ref.WeakReference;public class MainActivity extends AppCompatActivity { MyHandler myHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myHandler.sendEmptyMessageDelayed(1, 60000); } public void todo() { } static class MyHandler extends Handler { WeakReference<MainActivity> mWeakReference; public MyHandler(MainActivity activity) { mWeakReference=new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); mWeakReference.get().todo(); } }}
这样两个问题一下子解决了,而且没有警告
当然保险起见,两种方法同时使用是最保险的。
参考链接(内容都差不多)
http://blog.csdn.net/zhuanglonghai/article/details/38233069
http://blog.csdn.net/jdsjlzx/article/details/51386440
http://www.cnblogs.com/xujian2014/p/5025650.html
- Android内存泄漏之 handler
- Android 内存泄漏之handler
- Android Handler 内存泄漏
- Android Handler内存泄漏
- 【内存泄漏】Android内存泄漏---Handler
- Android Handler内存泄漏解决方法
- android Handler引起内存泄漏
- Android学习-Handler内存泄漏
- Android Handler 避免内存泄漏
- android handler leaks (内存泄漏)
- Android开发常见问题之Handler引起的内存泄漏
- Android之Handler内存泄漏分析及解决
- Android之Handler内存泄漏分析及解决
- Android之Handler内存泄漏分析及解决
- Android之Handler内存泄漏分析及解决
- Android Handler 避免内存泄漏之清空队列
- Android handler 引起的内存泄漏
- Android使用Handler防止内存泄漏
- Round A APAC Test 2017 Problem C. Jane's Flower Shop 二分、高精度要求
- 安卓Android单表通用数据库、万能数据库的设计,(sqlite,java反射,泛型),使用非常方便
- 关于2017开始的新工作想法与一些学习进步方向还有些阶段目标
- 常用的线程池
- 编码(三):Java Web编码问题
- Android 内存泄漏之handler
- 134. Gas Station
- SLIC超像素分割算法研究(代码可下载)
- 史上最详细最全面的ss-panel部署教程(V3版本)
- nyoj 14 会场安排问题
- IO流文件的复制删除
- R语言常用函数总结大全
- 线线几何位置关系
- 三种常用排序算法(冒泡、选择、快速)的Java实现