从名称说起,还有一个类似的常用名handle,先理解概念,再去看细节。
handle 常用翻译为 手柄、操作,计算机里面翻译为“句柄”,
定义:In computer programming, a handle is an abstract reference to a resource.
例如,进程号pid就是一个handle,文件描述符(fd)也是一个handle, 也就是用这个句柄可以访问、操作一些东西。其他用法待学习。
handler 处理者,管理者,搬运工,(信息)处理机
定义:Handler, an asynchronous callback (computer programming) subroutine in computing
初步总结
A handle is an abstract reference to a resource. Handle是对某个资源的抽象引用。
A handler is an asynchronous callback subroutine. Handler则是一个异步的回调函数(子程序)。
回到正题
android属于前端,展示层,更新UI的操作只能在主线程,那么耗时操作就用到了hanler, 异步去处理数据,再通过回调回到主线程去更新UI。
我们可以使用 Handler 发送并处理与一个线程关联的 Message 和 Runnable 。(注意:Runnable 会被封装进一个 Message,所以它本质上还是一个 Message )
每个 Handler 都会跟一个线程绑定,并与该线程的 MessageQueue 关联在一起,从而实现消息的管理以及线程间通信。
//在主线程中定义handler
android.os.Handler handler = new Handler(){
@Override
public void handleMessage(final Message msg) {
//这里接受并处理消息
}
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);
sendMessage和post本质上是一样的,底层都调用了sendMessageDelayed
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
//其中getPostMessage:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//Runnable 传给了callback
m.callback = r;
return m;
}
//这样,在最终looper中处理message的时候,最终都会调用handler的dispatchMessage函数:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//如下,直接调用了run方法,就执行了,不需要再去回到handleMessage方法去处理,本质上是更简单的写法,当然sendMessage时,也可以给message传入runnable,这样后续处理就一样了 Message message = Message.obtain(getHandler(), runnable);
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}:
private static void handleCallback(Message message) {
message.callback.run();
}
//在子线程中使用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();
}
}
二者的不同主要在于主线程中不需要创建Lopper
其中,handleMessage 对应定义中的回调。
Looper.prepare();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looper 提供了 Looper.prepare() 方法来创建 Looper ,并且会借助 ThreadLocal 来实现与当前线程的绑定功能。Looper.loop() 则会开始不断尝试从 MessageQueue 中获取 Message , 并分发给对应的 Handler
Looper.loop()
//Looper
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//...
for (;;) {
// 不断从 MessageQueue 获取 消息
Message msg = queue.next(); // might block
//退出 Looper
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
//...
}
//...
//回收 message, 见【3.5】
msg.recycleUnchecked();
}
}
还调用了 msg.target.dispatchMessage(msg) ,msg.target 就是发送该消息的 Handler,这样就回调到了 Handler 那边去了:
//Handler
public void dispatchMessage(Message msg) {
//msg.callback 是 Runnable ,如果是 post方法则会走这个 if
if (msg.callback != null) {
handleCallback(msg);
} else {
//callback 见【3.4】
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//回调到 Handler 的 handleMessage 方法
handleMessage(msg);
}
}
Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg) 回到了 Handler 来分发消息,以此来完成消息的回调。
初步小结
尝试小结一下它们的职责,如下:
Looper :负责关联线程以及消息的分发在该线程下**从 MessageQueue 获取 Message,分发给 Handler ;
MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。
用一句话总结:
Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。
线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。
by the way , 为什么主线程中Looper.loop()中的死循环没有造成卡死?
简单一句话是:Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。
两种用法:
post:
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final String response = get(url);
handler.post(new Runnable() {
@Override
public void run() {
//doSomeThing
}
});
}
}).start();
sendMessage:
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
//doSomeThing
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
}
});
view.post和handler.post区别
view.post其实内部是获取到了view所在线程(即ui线程)的handler,并且调用了handler的post方法.
在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类不会持有外部类的引用。
要解决这样的问题,我们在继承 Handler 的时候,要么是放在单独的类文件中,要么直接使用静态内部类。当需要在静态内部类中调用外部的 Activity 的时候,我们可以直接采用弱引用进行处理,所以我们大概修改后的代码如下。
private static final class MyHandler extends Handler{
private final WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity){
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mWeakReference.get();
if (activity != null){
// 开始写业务代码
}
}
}
private MyHandler mMyHandler = new MyHandler(this);
其实在我们实际开发中,不止一个地方可能会用到内部类,我们都需要在这样的情况下尽量使用静态内部类加弱引用的方式解决我们可能出现的内存泄漏问题。
用过 HandlerThread 吗?它是干嘛的?HandlerThread 是 Android API 提供的一个便捷的类,使用它可以让我们快速地创建一个带有 Looper 的线程,有了 Looper 这个线程,我们又可以生成 Handler,本质上也就是一个建立了内部 Looper 的普通 Thread。
我们在上面提到的子线程建立 Handler 大概代码是这样。
new Thread(new Runnable() {
@Override public void run() {
// 准备一个 Looper,Looper 创建时对应的 MessageQueue 也会被创建
Looper.prepare();
// 创建 Handler 并 post 一个 Message 到 MessageQueue
new Handler().post(new Runnable() {
@Override
public void run() {
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// Looper 开始不断的从 MessageQueue 取出消息并再次交给 Handler 执行
// 此时 Lopper 进入到一个无限循环中,后面的代码都不会被执行
Looper.loop();
}
}).start();
而采用 HandlerThread 可以直接把步骤简化为这样:
// 1. 创建 HandlerThread 并准备 Looper
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
// 2. 创建 Handler 并绑定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override
public void run() {
// 注意:Handler 绑定了子线程的 Looper,这个方法也会运行在子线程,不可以更新 UI
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// 3. 退出
@Override public void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
其中必须注意的是,workerThread.start() 是必须要执行的。
至于如何使用 HandlerThread 来执行任务,主要是调用 Handler 的 API。
使用 post 方法提交任务,postAtFrontOfQueue() 将任务加入到队列前端, postAtTime() 指定时间提交任务, postDelayed() 延后提交任务。
使用 sendMessage() 方法可以发送消息,sendMessageAtFrontOfQueue() 将该消息放入消息队列前端,sendMessageAtTime() 指定时间发送消息, sendMessageDelayed() 延后提交消息。
HandlerThread 的 quit() 和 quitSafety() 有啥区别?两个方法作用都是结束 Looper 的运行。它们的区别是,quit() 方法会直接移除 MessageQueue 中的所有消息,然后终止 MesseageQueue,而 quitSafety() 会将 MessageQueue 中已有的消息处理完成后(不再接收新消息)再终止 MessageQueue。
https://juejin.im/post/5a13a34f51882575d42f004f
参考:
https://juejin.im/post/5c74b64a6fb9a049be5e22fc
https://www.zhihu.com/question/34652589/answer/90344494