从接触Handler到现在接近4年,慢慢深入 慢慢摸索,直到今天看完何红辉的源码讲解之后终于知道具体
最初了解到handler是因为几个异常
第一个是 子线程不能操控UI线程的控件 需要用handler.post(Runnable) 将其推到主线程消息队列中执行
第二个是 直接在子线程中new handler()时抛出 cann't create handler inside thread that has not called Looper.prepare() 第一次知道有个Looper 这东西
好的 先上图
各个部件职能说明:
Thread:线程对象 申请一个线程运行空间,独立运行子线程
Handler :处理者 提供给外部的一个句柄,用来操控消息队列 和循环器 并执行消息具体处理
MessageQueue: 消息队列 用于存放消息 采用队列先进先出原则
Looper : 循环器 内部是一个死循环,循环读取MessageQueue中的消息,如果消息队列为空 那么阻塞
解释一下图:
1.线程中有保留handler对象的引用
2.handler中保留looper和MessageQueue的引用
3.Looper中保留MessageQueue的引用
保留引用的作用就是可以调用
而这个机制很重要的一点就是 :
Thread中的Handler对象被公开了, 也就是说在外部可以获取此线程的handler, 那么 就可以操控此线程的Looper和MessageQueue
有这个基础 我们讲下 消息处理过程(sendMessage,handleMessage过程 )
1. 首先由handler向MessageQueue中推送一条消息
2. 然后Looper阻塞的线程恢复运行,读取MessageQueue中的消息,将消息发给Handler (这里有个问题 Looper并没有handler的引用 ,如何将消息发给handler)继续等待/处理下一条消息
3. Handler调用我们自定义的处理方法处理消息
开始讲代码
一、首先看线程创建过程(以主线程为例 因为主线程中会创建Looper)
线程创建时主要做了这三件事 (主要讨论handler相关)
Thread()
{
Looper.prepare(); //在 线程中创建一个循环器looper
handler=thread.getHandler(); //在线程中创建Handler()
Looper.loop(); //开始死循环
}
1.1 首先看Looper.prepare() 主要做了什么
public static void prepare()
{
//Looper中保留了线程的引用 即这里的threadLocal
if(threadLocal.get()!=null)
{
//判断当前线程有没有创建过Looper get()方法就是获取looper对象
throw new RuntimeException("only one Looper may be created per thread");
}
threadLocal.set(new Looper);
}
很简单, prepare函数只简单地创建了一个Looper
1.2 然后是
get
Handler()函数
Handler 是 直接new出来的 我们看下它的构造函数
public Handler()
{
mLooper=Looper.myLooper(); //Looper中提供静态方法 myLooper 获取当前looper
if(mLooper==null)
{
throw new RuntimeException("Can't not create handler inside thread that has not called Looper.prepare");
}
mQueue=mLooper.mQueue;
mCallback=null;
}
很明显 handler中有 Looper 和MessageQueue的引用 mLooper 和 mQueue 在构造函数中初始化了这两个引用
然后中间的那个异常 就是说 必须要调用Looper.prepare()预先创建好Looper 否则 后面 从Looper中获取mQueue 时肯定会报空指针异常
这里主要就是创建handler 并初始化两个引用
1.3 创建Thread的最后一步 : Looper.loop(); 执行循环
public static void loop()
{
Looper me=myLooper(); //myLooper函数 同上
if(me==null)
{
throw new RuntimeException("No Looper ; Looper.prepare wasn't called on this thread");
}
MessageQueue queue=me.mQueue; //同上 获取消息队列对象
while(true) //死循环 读取消息
{
Message msg= queue.next(); //获取 下一条消息 如果没有就阻塞
//判断消息是否为空
if(msg!=null)
{
if(msg.target==null)
{
//target是发送消息的handler对象(handler 发送消息的同时 把自己也保存在了消息对象里面), 如果发送这个msg的handler不存在了(被销毁)这个消息就不处理了
return ;
}
msg.target.dispatchMessage(msg); //将消息转发给handler处理 (这里有点绕 handler把消息发过来 这里又把消息发回 handler 有病吧…… 呵呵 后面再解释)
msg.recycle(); //消息对象回收
}
}
}
以上 便是建立线程时 初始化handler机制的三个步骤:
1 创建Looper
2 创建Handler
3 启动Looper;
是不是感觉少了什么? 怎么没找到MessageQueue创建在哪?
其实MessageQueue是包含在Looper里面的 创建Looper的同时就创建了MessageQueue 上面获取MessageQueue 都是通过Looper获取的
其实到现在 大部分流程都已经清楚了 那么 开始解释上面的 绕吧!
不懂得地方就是这句
msg.target.dispatchMessage(msg);
我们先看下Message对象里面到底有什么东西
Message
{
Handler target; //target是handler类型 其实也就是发送此消息的那个handler //哈哈 还记得上面的问题 Looper内没有handler的引用 怎么将消息发给handler么?答案就在Message内 它保存了发送方的引用
//另外记得handler的一个方法么:handler.obtainMessage().sendToTarget()
Runnable callback; //Runnable 对象 想必是要运行什么 先记着 Message next ; //下一条消息 消息队列是链式存储的 int arg1, arg2 ,what ; Object obj; ...............}
那么 上面就是调用了handler的dispatchMessage方法
public void dispatchMessage(Message msg)
{
//好的 这里就用上了 上面的Runnble类型的Callback
if(msg.callback !=null)
{
//当msg中的Callback不为空时就调用msg中的Runnale callback
handleCallback(msg);
}else
{
if(mCallback!=null)
{
if(mCallback.handleMessage(msg)){
{
return ;
}
}
//如果Message没自带处理方法就调用 handler的handleMessage方法 这个方法我们一般会覆盖它
handleMessage(msg);
}
}
private final void handleCallback(Message message)
{
message.callback.run();
}
public void handleMessage(Message msg)
{
//这个方法我们一般会覆盖它 用它来处理我们发送的信息
}
到这里 我们还是不清楚
Runnable是在哪用到的 那么 提示下
我们使用handler有两种方式:
(1) handler.sendMessage();
(2) handler.post(Runnable);
哈哈 再知道那个Runnable callback 哪里来的吧 这两种方式就是上面的if /else
其实这两种方式是一样的 都是通过sendMessageDelay()方法将Message对象发送到MessageQueue中 , sendMessage发送的是个包装好的Message对象 而post方法是将Runnable封装成了Message对象
好的 还剩最后一个问题
handler将消息发给消息队列 Looper取出来 又分给handler 有病吧!!! 还不如直接给handler处理……
我们分析下
我们利用handler主要是为了跨线程传递消息 ,
而这里handler将消息发给消息队列后 其实就是一个切换线程的过程 由原来的线程转到了handler所在线程的内部looper循环 ,
如果直接交给handler处理就还是在原来的线程中操作。
另外通过消息队列一转换 由同步过程变成异步过程
一是解耦
二是 解放了handler handler不必直接进行处理操作(如果这个操作很耗时 那么多次调用handler可能会出现异常情况)
好了 handler机制到此结束 此过程部分为主观臆断 如果有什么不对 麻烦请给我指正