Handler
的通常用法 = 新建Handler
子类(内部类) 、匿名Handler
内部类/** * 方式1:新建Handler子类(内部类) */
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程建立时便自动建立Looper & 对应的MessageQueue
// 以后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 实例化自定义的Handler类对象->>分析1
//注:此处并没有指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue
showhandler = new FHandler();
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
// 分析1:自定义Handler子类
class FHandler extends Handler {
// 经过复写handlerMessage() 从而肯定更新UI的操做
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
}
}
/** * 方式2:匿名Handler内部类 */
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程建立时便自动建立Looper & 对应的MessageQueue
// 以后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 经过匿名内部类实例化的Handler类对象
//注:此处并没有指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue
showhandler = new Handler(){
// 经过复写handlerMessage()从而肯定更新UI的操做
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
};
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
}
复制代码
测试结果java
- 警告的缘由 = 该
Handler
类因为无设置为 静态类,从而致使了内存泄露- 最终的内存泄露发生在
Handler
类的外部类:MainActivity
类
那么,该Handler
在无设置为静态类时,为何会形成内存泄露呢?ide
Looper
对象的生命周期 = 该应用程序的生命周期Java
中,非静态内部类 & 匿名内部类都默认持有 外部类的引用从上述示例代码可知:oop
Handler
实例的消息队列有2个分别来自线程一、2的消息(分别 为延迟1s
、6s
)Handler
消息队列 还有未处理的消息 / 正在处理消息时,消息队列中的Message
持有Handler
实例的引用Handler
= 非静态内部类 / 匿名内部类(2种使用方式),故又默认持有外部类的引用(即MainActivity
实例),引用关系以下图上述的引用关系会一直保持,直到
Handler
消息队列中的全部消息被处理完毕测试
Handler
消息队列 还有未处理的消息 / 正在处理消息时,此时若需销毁外部类MainActivity
,但因为上述引用关系,垃圾回收器(GC)
没法回收MainActivity
,从而形成内存泄漏。以下图:Handler
消息队列 还有未处理的消息 / 正在处理消息时,存在引用关系: “未被处理 / 正处理的消息 -> Handler
实例 -> 外部类” Handler
的生命周期 > 外部类的生命周期 时(即 Handler
消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁时),将使得外部类没法被垃圾回收器(GC)
回收,从而形成 内存泄露从上面可看出,形成内存泄露的缘由有2个关键条件:this
Handler
实例 -> 外部类” 的引用关系Handler
的生命周期 > 外部类的生命周期即
Handler
消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁spa
解决方案的思路 = 使得上述任1条件不成立 便可。线程
原理
静态内部类 不默认持有外部类的引用,从而使得 “未被处理 / 正处理的消息 -> Handler
实例 -> 外部类” 的引用关系 的引用关系 不复存在。3d
具体方案
将Handler
的子类设置成 静态内部类code
- 同时,还可加上 使用WeakReference弱引用持有Activity实例
- 缘由:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程建立时便自动建立Looper & 对应的MessageQueue
// 以后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 实例化自定义的Handler类对象->>分析1
//注:
// a. 此处并没有指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
// b. 定义时需传入持有的Activity实例(弱引用)
showhandler = new FHandler(this);
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
// 分析1:自定义Handler子类
// 设置为:静态内部类
private static class FHandler extends Handler{
// 定义 弱引用实例
private WeakReference<Activity> reference;
// 在构造方法中传入需持有的Activity实例
public FHandler(Activity activity) {
// 使用WeakReference弱引用持有Activity实例
reference = new WeakReference<Activity>(activity); }
// 经过复写handlerMessage() 从而肯定更新UI的操做
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
}
}
复制代码
原理
不只使得 “未被处理 / 正处理的消息 -> Handler
实例 -> 外部类” 的引用关系 不复存在,同时 使得 Handler
的生命周期(即 消息存在的时期) 与 外部类的生命周期 同步cdn
具体方案
当 外部类(此处以Activity
为例) 结束生命周期时(此时系统会调用onDestroy()
),清除 Handler
消息队列里的全部消息(调用removeCallbacksAndMessages(null)
)
具体代码
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
// 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
}
复制代码
为了保证Handler
中消息队列中的全部消息都能被执行,此处推荐使用解决方案1解决内存泄露问题,即 静态内部类 + 弱引用的方式
Handler
形成 内存泄露的相关知识:原理 & 解决方案