乐趣区

Handler 浅析

Handler
一、简介
Handler 是用来结合线程的消息队列来发送、处理“Message 对象”和“Runnable 对象”的工具。每一个 Handler 实例之后会关联一个线程和该线程的消息队列。当你创建一个 Handler 的时候,从这时开始,它就会自动关联到所在的线程 / 消息队列,然后它就会陆续把 Message/Runnalbe 分发到消息队列,并在它们出队的时候处理掉。简单说,它是一套 Android 消息传递机制。
二、用途

延时操作。推送未来某个时间点将要执行的 Message 或者 Runnable 到消息队列;
线程之间的通讯。将消息推送到相应线程的消息队列中,等待处理。

三、使用方法
(1)sendMessage
// ************ 不配合线程使用 *************
// 获取一个 Message 对象,设置 what 为 1
Message msg = Message.obtain();
msg.obj = data;
msg.what = IS_FINISH;
mHandler.sendMessage(msg);

// 接受并处理
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
tx.setText(msg.what + “”);
}
};

// ************ 配合线程使用 *************
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Toast.makeText(MainActivity.this, “ 刷新 UI、”, Toast.LENGTH_SHORT).show();
}
}
};

new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
mHandler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
常用 API:

boolean sendMessage (Message msg) : 发送消息。
boolean sendEmptyMessage (int what) : 直接拿到一个空的消息,并赋值 what,然后发送到 MessageQueue。
boolean sendMessageDelayed (Message msg, long delayMillis) : 在延迟 delayMillis 毫秒之后发送一个 Message。
boolean sendMessageAtTime (Message msg, long uptimeMillis) : 在某个时间点执发送消息。
void removeMessages (int what) : 移除所有 what 值得 Message 对象。

(2)post
// ************ 不配合线程使用 *************
private Handler mHandler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
tvMessage.setText(“ 使用 Handler.post 在工作线程中发送一段执行到消息队列中,在主线程中执行。”);
}
});

// ************ 配合线程使用 *************
new Thread(new Runnable() {
@Override
public void run() {
// 在子线程中实例化 Handler 同样是可以的,只要在构造函数的参数中传入主线程的 Looper 即可
Handler handler = new Handler(Looper.getMainLooper());
// 通过 Handler 的 post Runnable 到 UI 线程的 MessageQueue 中去即可
handler.post(new Runnable() {
@Override
public void run() {
// 在 MessageQueue 出队该 Runnable 时进行的操作
tvMessage.setText(“ 使用 Handler.post 在工作线程中发送一段执行到消息队列中,在主线程中执行。”);
}
});
}
}).start();
常用 API:

boolean post (Runnable r) : 将 Runnable 对象加入 MessageQueue。
boolean postAtTime (Runnable r, Object token, long uptimeMillis) : 在某个时间点执行 Runnable r。
boolean postDelayed (Runnable r, long delayMillis) : 当前时间延迟 delayMillis 个毫秒后执行 Runnable r。
void removeCallbacks (Runnable r, Object token) : 移除 MessageQueue 中的所有 Runnable 对象。
void removeCallbacksAndMessages (Object token) : 移除 MessageQueue 中的所有 Runnable 和 Message 对象。

四、原理
Handler 的初始化,其实是初始化 Looper 和 MessageQueue。通过 Looper.Prepare 实例化 Looper 和 MessageQueue,并将 Looper 设置进 ThreadLocal(所以,使用 Handler 之前一定要调用 Looper.Prepare)。ThreadLocal.set 方法的作用是将设置的值(这里是当前初始化的 Looper)与当前线程进行绑定(当前线程就是调用 Looper.prepare 的线程)。主线程中没有显式的调用 Looper.Prepare 是因为,Android 系统已经帮我们调用了 Looper.Prepare。
Handler 在调用 sendMessage 的时候主要做了两件事:1. 将自己设置给 Message 的 Target 变量,从而将 Handler 与 Message 绑定在一起;2. 将 Message 放入 MessageQueue 队列中。
Looper.loop 从 MQ 中轮训获取消息,消息不为 Null 则回调 disPatchMessage 方法(Runnable 对象 OR handlerMessage)。
总结:

Handler:负责发送和处理消息。
Message:用来携带需要的数据。
MessageQueue:消息队列,用来存储 Message 的。
Looper:消息轮巡器,负责不停的从 MessageQueue 中取 Message。

子线程中使用 Handler:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
五、Handler 引发的内存泄漏
外部类 Activity 中定义了一个非静态内部类 Handler,非静态内部类默认持有对外部类的引用。如果外部 Activity 突然关闭了,但是 MessageQueue 中的消息还没处理完,那么 Handler 就会一直持有对外部 Activty 的引用,垃圾回收器无法回收 Activity,从而导致内存泄漏。
解决方法:1. 停掉线程(切断了与 Activity 之间的关联)或移除 removexxx 消息;2. 将 Handler 声明为静态类。改成静态内部类后,对外部类的引用设为弱引用,在垃圾回收时,会自动将弱引用的对象回收。如:
public class HandlerActivity extends AppCompatActivity {
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable mRunnable = new Runnable() {
@Override
public void run() {
// 操作
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
mHandler.postDelayed(mRunnable, 1000*10);
finish();
}

private static class MyHandler extends Handler {
WeakReference<HandlerActivity> mWeakActivity;

public MyHandler(HandlerActivity activity) {
this.mWeakActivity = new WeakReference<HandlerActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
final HandlerActivity mActivity = mWeakActivity.get();
if (mActivity != null) {
// 处理消息
}
}
}
}
六、HandlerThread
HandlerThread 是一种线程,它和普通的 Thread 之间的区别就是 HandlerThread 在创建的时候会提供自己该线程的 Looper 对象,不需要手动创建 Looper。
HandlerThread handlerThread = new HandlerThread(“downloadImage”); // 当前线程的名字,可以任意字符串
handlerThread.start(); // 必须先开启线程
/**
* 该 callback 运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
// 在子线程中进行相应的网络请求

// 通知主线程去更新 UI
mUIHandler.sendMessage(msg1);
return false;
}
}
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

final HandlerThread downloadAThread = new HandlerThread(“downloadAThread”);
downloadAThread.start();
Handler downloadAHandler = new Handler(downloadAThread.getLooper());
// downloadAHandler 子线程的 Handler
downloadAHandler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), “ 下载 A 完成 ”, Toast.LENGTH_SHORT).show();
mainHandler.post(new Runnable() {
@Override
public void run() {
tv_A.setText(“A 任务已经下载完成 ”);
}
});
}
}, 1000 * 5);
当我们不需要 HandlerThread 的时候,我们可以调用 quitSafely() 或者 quit() 方法来结束这个线程。
参考文章:Android 基础夯实 – 你了解 Handler 有多少?,Android 面试常客之 Handler 全解

退出移动版