本来应该被回收的对象不能被回收而停留在堆内存中。
当一个对象实例不再被使用时,正常来说应该被回收,但却因为有另外一个正在使用的对象持有它的引用,从而导致它不能被回收。这就导致了内存溢出。
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();
}
}
上面是通过非静态内部类的方式来使用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();
}
}
使用匿名内部类的方式IDE更是直接地给出了提示:
意思是说Handler应该设置为static否则可能会发生溢出。
上面两个例子里的Handler都可以直接使用外部类里的任何变量和方法,为什么呢?因为在Java中,非静态内部类和匿名内部类都默认持有外部类的引用。
主线程的Looper循环器对象的生命周期 = 该应用程序的生命周期
Handler异步消息机制中有3个重要的类, 它们的关系如下:
当Handler消息队列还有未处理的消息或正在处理消息时,消息队列中的Message消息对象持有Handler实例的引用,Handler又持有外部类的引用(即MainActivity实例)。此时若销毁外部类MainActivity,那是不可能成功的。因为MainActivity在需要销毁时还被引用着,所以垃圾回收器(GC)无法回收MainActivity,从而造成内存溢出。
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;
}
}
}
完整代码如下:
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();
}
}
(2)当外部类结束生命周期时,清空Handler内消息队列
改进后的代码:
@Override
protected void onDestroy() {
super.onDestroy();
// 外部类Activity生命周期结束时,同时清空消息队列 和 结束Handler生命周期
myHandler.removeCallbacksAndMessages(null);
}
完成代码如下:
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);
}
}
谢谢阅读。