一、Message 消息
模仿 Android 中的 Message 基本功能 , 提供 what 与 obj 变量 , 提供一个回收方法 ;
此外 , 还要指明下一个消息 , 以及是哪个 Handler 发送的该消息 ;
package kim.hsl.handler; public class Message { /** * 消息识别码 */ int what; /** * 消息对象 */ Object obj; /** * 指向下一个消息 */ Message next; /** * 该 Message 使用哪个 Handler 进行发送的 */ Handler target; /** * 回收方法 */ public void recyle(){ obj = null; next = null; target = null; } }
二、Handler 消息处理者
Handler 有两个功能 :
功能一 : 发送消息到 Looper 中的 消息队列 MessageQueue 中 ;
/** * 发送消息 * @param msg */ public void sendMessage(Message msg){ // 为消息设置发送的 Handler msg.target = this; // 向消息队列中放入要执行的消息 mQueue.enqueueMessage(msg); }
功能二 : 接收 Looper 中的 loop 方法传来的 Message , 并 执行该 Message 代表的任务 ;
Handler 执行 Message 任务 , 具体的执行逻辑需要 用户实现 ; 用户创建 Handler 时 , 需要覆盖 handleMessage 方法 , 在重写的方法中处理不同的 Message 任务 ;
/** * 执行消息对应的任务 * @param next */ public void handleMessage(Message next) { }
Handler 初始化 :
Handler 的功能一 发送消息 , 就是向 消息队列 MessageQueue 中发送消息 , 并将消息放到 MessageQueue 中的 Message 链表队列的最后一个 ;
这就需要 Handler 持有 消息队列 MessageQueue 的引用 ,
消息队列封装在 Looper 中 , 因此需要先拿到 线程本地变量 Looper , 然后从 Looper 中获取对应的消息队列 ;
这里就需要特别注意 , 在初始化 Handler 时 , 需要用到 Looper , 如果 Looper 为空 , Handler 初始化就会失败 ;
因此在 创建 Handler 之前 , 必须先调用 Looper 的 prepare 方法 , 先将 Looper 进行初始化操作 ;
/** * 消息队列 * 该消息队列封装在 Looper 中 * Looper 封装在线程本地变量中 */ MessageQueue mQueue; public Handler(){ /* 在 Handler 中需要拿到 Looper 进而拿到 Looper 中的 MessageQueue 消息队列 Handler 的操作就是将 Message 放入 MessageQueue 因此在 Handler 中需要持有 MessageQueue 消息队列的引用 获取 Looper 时 , Looper 必须已经初始化完毕, 也就是已经调用过 prepare 创建了 Looper 并将其放入了线程本地变量 */ // 获取当前线程中的 线程本地变量 Looper Looper looper = Looper.looper(); // 获取封装在 Looper 中的 消息队列 MessageQueue mQueue = looper.mQueue; }
完整 Handler 代码 :
package kim.hsl.handler; public class Handler { /** * 消息队列 * 该消息队列封装在 Looper 中 * Looper 封装在线程本地变量中 */ MessageQueue mQueue; public Handler(){ /* 在 Handler 中需要拿到 Looper 进而拿到 Looper 中的 MessageQueue 消息队列 Handler 的操作就是将 Message 放入 MessageQueue 因此在 Handler 中需要持有 MessageQueue 消息队列的引用 获取 Looper 时 , Looper 必须已经初始化完毕, 也就是已经调用过 prepare 创建了 Looper 并将其放入了线程本地变量 */ // 获取当前线程中的 线程本地变量 Looper Looper looper = Looper.looper(); // 获取封装在 Looper 中的 消息队列 MessageQueue mQueue = looper.mQueue; } /** * 发送消息 * @param msg */ public void sendMessage(Message msg){ // 为消息设置发送的 Handler msg.target = this; // 向消息队列中放入要执行的消息 mQueue.enqueueMessage(msg); } /** * 执行消息对应的任务 * @param next */ public void handleMessage(Message next) { } }
三、MessageQueue 消息队列
Message 链表 : 消息队列 MessageQueue , 内部维护了一个 Message 链表 , 存储的时候只存储第一个 Message 即可 ;
链表插入元素 : 当 Handler 在其它线程调用 sendMessage 方法 , 将 消息 Message 放入 Looper 中的 MessageQueue 时 , 针对该链表的操作就是 , 循环获取链表的下一个元素 , 最终 获取到最后一个元素 , 最后一个元素的 next 为空 ; 将 最后一个元素的 next 设置为本次要插入的 Message , 即可完成消息存储到消息队列的操作 ;
链表元素同步 : 链表为空时 , 取出链表的操作会阻塞 , 调用的是 wait 方法 , 此时有消息加入链表后 , 需要 调用 notify 唤醒阻塞 ;
消息入队的部分代码 :
/** * 该队列是一个链表 , 因此这里只给出第一个 Message 即可 */ Message mMessage; /** * 将 Message 消息加入到 Message 链表中 * @param msg */ public void enqueueMessage( Message msg ){ // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息 // 因此 需要使用同步代码块包裹以下逻辑 synchronized (this){ if( mMessage == null ){ mMessage = msg; }else{ /* 如果链表不为空 这里需要循环查找消息队列的最后一个消息 将本次传入的 Message msg 参数加入到链表尾部 */ Message pointer = mMessage; Message previous = pointer; for(;;){ // 记录上一条消息, 每次遍历都将本次遍历的记录下来 previous = pointer; // 将 pointer 指向下一条消息 pointer = pointer.next; // 此时如果某个 Message 的 下一个元素为空 // 说明该 Message 是消息队列最后一个元素 if(pointer == null){ break; } } // 将本次参数传入的 Message 放到链表最后 previous.next = msg; } notify(); } }
Looper 调用 loop 方法后 , 会一直循环 , 不断地从 消息队列 MessageQueue 中取出 Message 消息 , 然后 将 Message 消息发送给对应的 Handler 执行对应的操作 ;
从 消息队列 MessageQueue 中取出消息 , 也是 取出链表表头 的操作 , 取出该链表的表头 , 然后 将表头设置成链表的第二个元素 ;
消息同步 : 如果当前链表为空 , 此时会 调用 wait 方法阻塞 , 直到消息入队时 , 链表中有了元素 , 会调用 notify 解除该阻塞 ;
/** * 从消息队列中获取消息 * @return */ public Message next(){ synchronized (this){ // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中 Message result; for (;;){ // 尝试和获取 消息队列 链表中的第一个元素 result = mMessage; if(result == null){ // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可 break; } } // 处理链表逻辑 , 将表头指向下一个 Message mMessage = mMessage.next; return result; } }
消息队列完整代码 :
package kim.hsl.handler; public class MessageQueue { /** * 该队列是一个链表 , 因此这里只给出第一个 Message 即可 */ Message mMessage; /** * 将 Message 消息加入到 Message 链表中 * @param msg */ public void enqueueMessage( Message msg ){ // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息 // 因此 需要使用同步代码块包裹以下逻辑 synchronized (this){ if( mMessage == null ){ mMessage = msg; }else{ /* 如果链表不为空 这里需要循环查找消息队列的最后一个消息 将本次传入的 Message msg 参数加入到链表尾部 */ Message pointer = mMessage; Message previous = pointer; for(;;){ // 记录上一条消息, 每次遍历都将本次遍历的记录下来 previous = pointer; // 将 pointer 指向下一条消息 pointer = pointer.next; // 此时如果某个 Message 的 下一个元素为空 // 说明该 Message 是消息队列最后一个元素 if(pointer == null){ break; } } // 将本次参数传入的 Message 放到链表最后 previous.next = msg; } notify(); } } /** * 从消息队列中获取消息 * @return */ public Message next(){ synchronized (this){ // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中 Message result; for (;;){ // 尝试和获取 消息队列 链表中的第一个元素 result = mMessage; if(result == null){ // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可 break; } } // 处理链表逻辑 , 将表头指向下一个 Message mMessage = mMessage.next; return result; } } }