Android性能优化之Handler内存溢出

举报
yd_221104950 发表于 2020/12/04 23:16:39 2020/12/04
【摘要】 内存溢出的定义 本来应该被回收的对象不能被回收而停留在堆内存中。 内存溢出的原因 当一个对象实例不再被使用时,正常来说应该被回收,但却因为有另外一个正在使用的对象持有它的引用,从而导致它不能被回收。这就导致了内存溢出。 由Handler引起的内存溢出问题 Handler的一般用法是在Activity中新建Handler子类(内部类)或匿名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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。