Android性能优化之Handler内存溢出
内存溢出的定义
本来应该被回收的对象不能被回收而停留在堆内存中。
内存溢出的原因
当一个对象实例不再被使用时,正常来说应该被回收,但却因为有另外一个正在使用的对象持有它的引用,从而导致它不能被回收。这就导致了内存溢出。
由Handler引起的内存溢出问题
Handler的一般用法是在Activity中新建Handler子类(内部类)或匿名Handler内部类。
我们分析下面这个例子:
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
/**
* 方式1:新建Handler子类(内部类)
*/
public class MainActivity extends AppCompatActivity { // 主线程创建时便自动创建Looper和对应的MessageQueue // 之后执行Loop()进入消息循环 class MyHandler extends Handler { // 通过重写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d("MyHandler", "收到线程1的消息"); break; case 2: Log.d("MyHandler", " 收到线程2的消息"); break; } } } private MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. 实例化自定义的Handler类对象 // 此处不需要指定Looper,因为在主线程中创建Handler已自动绑定当前线程(主线程)的Looper、MessageQueue myHandler = new MyHandler(); // 2. 启动子线程1 new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 1;// 消息标识 msg.obj = "Tea";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); // 3. 启动子线程2 new Thread() { @Override public void run() { try { Thread.sleep(5000);// 睡5秒 } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 2;// 消息标识 msg.obj = "Water";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
上面是通过非静态内部类的方式来使用Handler,下面我们看看以匿名内部类的方式:
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
/**
* 方式2:匿名内部类
*/
public class MainActivity extends AppCompatActivity { // 主线程创建时便自动创建Looper和对应的MessageQueue // 之后执行Loop()进入消息循环 private Handler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. 实例化自定义的Handler类对象 // 此处不需要指定Looper,因为在主线程中创建Handler已自动绑定当前线程(主线程)的Looper、MessageQueue myHandler = new Handler() { // 通过复写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d("MyHandler", "收到线程1的消息"); break; case 2: Log.d("MyHandler", " 收到线程2的消息"); break; } } }; // 2. 启动子线程1 new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 1;// 消息标识 msg.obj = "Tea";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); // 3. 启动子线程2 new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 2;// 消息标识 msg.obj = "Water";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
使用匿名内部类的方式IDE更是直接地给出了提示:
意思是说Handler应该设置为static否则可能会发生溢出。
上面两个例子里的Handler都可以直接使用外部类里的任何变量和方法,为什么呢?因为在Java中,非静态内部类和匿名内部类都默认持有外部类的引用。
主线程的Looper循环器对象的生命周期 = 该应用程序的生命周期
Handler异步消息机制中有3个重要的类, 它们的关系如下:
- Handler对象绑定着一个MessageQueue实例,就是Handler对象里持有一个MessageQueue的实例,Handler发送的消息就是装入这个消息队列对象里的。
- Looper循环器持有MessageQueue的引用。
- Message消息对象持有Handler对象的引用,所以Looper循环器取出一个消息对象时,就能通过消息对象Message持有的Handler对象,调用Handler对象的dispatchMessage方法,dispatchMessage方法里会调用Handler对象的handleMessage方法。
溢出原因
当Handler消息队列还有未处理的消息或正在处理消息时,消息队列中的Message消息对象持有Handler实例的引用,Handler又持有外部类的引用(即MainActivity实例)。此时若销毁外部类MainActivity,那是不可能成功的。因为MainActivity在需要销毁时还被引用着,所以垃圾回收器(GC)无法回收MainActivity,从而造成内存溢出。
Handler造成内存溢出的原因总结:
- 当Handler消息队列还有未处理的消息或正在处理消息时,存在引用关系: “未被处理 / 正处理的消息 -> Handler实例 -> 外部类”,导致外部类需销毁时无法销毁,从而造成内存溢出。
解决方案
(1)静态内部类+弱引用
为了保证Handler中消息队列中的所有消息都能被执行,推荐此方式来解决内存溢出问题:
静态内部类+弱引用:
private static class MyHandler extends Handler{ // 定义弱引用实例 private WeakReference<MainActivity> mainActivityWeakReference; public MyHandler(MainActivity activity){ // 使用WeakReference弱引用持有Activity实例 this.mainActivityWeakReference = new WeakReference<>(activity); } // 通过重写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Log.d("MyHandler", "收到线程1的消息"); break; case 2: Log.d("MyHandler", " 收到线程2的消息"); break; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
完整代码如下:
package com.ti.controlsound;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* 方式1:新建Handler子类(内部类)
*/
public class MainActivity extends AppCompatActivity { // 主线程创建时便自动创建Looper和对应的MessageQueue // 之后执行Loop()进入消息循环 private MyHandler myHandler; private static class MyHandler extends Handler{ // 定义弱引用实例 private WeakReference<MainActivity> mainActivityWeakReference; public MyHandler(MainActivity activity){ // 使用WeakReference弱引用持有Activity实例 this.mainActivityWeakReference = new WeakReference<>(activity); } // 通过重写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Log.d("MyHandler", "收到线程1的消息"); break; case 2: Log.d("MyHandler", " 收到线程2的消息"); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. 实例化自定义的Handler类对象->>分析1 // 注:此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue myHandler = new MyHandler(this); // 2. 启动子线程1 new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 1;// 消息标识 msg.obj = "Tea";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); // 3. 启动子线程2 new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 2;// 消息标识 msg.obj = "Water";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
(2)当外部类结束生命周期时,清空Handler内消息队列
改进后的代码:
@Override protected void onDestroy() { super.onDestroy(); // 外部类Activity生命周期结束时,同时清空消息队列 和 结束Handler生命周期 myHandler.removeCallbacksAndMessages(null); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
完成代码如下:
package com.ti.controlsound;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
/**
* 方式1:新建Handler子类(内部类)
*/
public class MainActivity extends AppCompatActivity { // 主线程创建时便自动创建Looper和对应的MessageQueue // 之后执行Loop()进入消息循环 private Handler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. 实例化自定义的Handler类对象->>分析1 // 注:此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue myHandler = new Handler() { // 通过复写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d("MyHandler", "收到线程1的消息"); break; case 2: Log.d("MyHandler", " 收到线程2的消息"); break; } } }; // 2. 启动子线程1 new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 1;// 消息标识 msg.obj = "Tea";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); // 3. 启动子线程2 new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 定义要发送的消息 Message msg = Message.obtain(); msg.what = 2;// 消息标识 msg.obj = "Water";// 消息存放 // 传入主线程的Handler,利用myHandler向其MessageQueue发送消息 myHandler.sendMessage(msg); } }.start(); } @Override protected void onDestroy() { super.onDestroy(); // 外部类Activity生命周期结束时,同时清空消息队列 和 结束Handler生命周期 myHandler.removeCallbacksAndMessages(null); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
谢谢阅读。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/96109154
- 点赞
- 收藏
- 关注作者
评论(0)