博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Message,MessageQueue,Handler,Looper,Thread,ThreadLocal
阅读量:5111 次
发布时间:2019-06-13

本文共 4567 字,大约阅读时间需要 15 分钟。

Message:单个消息体,承载了两个线程之间交流的信息。不同的消息由"what"来区分。

MessageQueue:消息链表,所有从非UI线程发过来的Message都被加入到这个队列中。其实这个类主要功能就是往mMessage的next加入新的消息,然后提供给Looper获取。

Handler:在非主线程中发送消息,并且在主线程中处理消息。(自己发送给自己,好变态。)

Looper: 遍历MessageQueue中所有没有处理过的消息,发送给Handler处理。

Thread:非主线程。上面所有的东西都是为了该线程和主线程交互数据(比如View)做准备的。

总体流程如下:

1 UI线程{ 2            //申请Handler对象。 3            Handler mHandler =new Handler(){ 4             5               @Override 6               public void handleMessage(Message msg) { 7                8                 9               }10            };11            //非主线程12            Thread{13                run(){14                                       15                    0: Handler发送Message16                    1:Message被塞入MessageQueue17                    2: Looper 不断的遍历MessageQueue,取出消息。18                    3:在Looper中回调Handler的 handleMessage(Message msg) 函数。19                    20                     21                     22                }23            }24            //整个过程又回到了UI线程中处理。25         }

总结上面的流程:Handler并没有真正开启另外一个线程,两个线程中通过一个消息队列,实现了数据在两个线程中的"同步"传递。

当然如果只是为了在不同线程间传递数据,直接使用共享变量就可以了。例如可以将Handler的类设置成一个Object对象即可。事实上Handler本身就是个线程中的共享变量。
真正有意义的地方在于:Looper是一个死循环,不断的遍历MessageQueue里面的Message,传递给UI线程的Handler,然后即时的影响UI线程的View的绘画等。整个过程让人感觉是同步操作。

应用层代码:

1 //注册一个Handler 2      private Handler mHandler =new Handler(){ 3   4         @Override 5          public void handleMessage(Message msg) { 6             //do some stuffs 7          } 8           9       };10       //启动一个新的线程:11        new Thread(new Runnable()12          {13               14               15               @Override16               public void run() {17                 //在当前线程中生成一个新的Looper对象18                 Looper.prepare();19                 20                 //给MessageQueue队列插入一条消息。21                 mHandler.sendEmptyMessage(0);22                 23                 //不断的轮询遍历MessageQueue的消息,并且发送给mHandler24                 Looper.loop();25                }26               27 28         })start();

源码:

上面的流程中没有看到Looper,MessageQueue,Handler三者之间是怎么建立起练习的。下面部分做出解释:

1 Looper.java: 2     public static void prepare() { 3          //生成了一个新的Looper对象并且保存在sThreadLocal的map中。注意这个map的键key是当前线程的标示,也就是Thread.currentThread()。 4          //所以每个线程都有一个独立的Looper对象。 5          sThreadLocal.set(new Looper()); 6           7     } 8     //在Looper对象的初始化中会创建一个新的MessageQueue 9     private Looper() {10         mQueue = new MessageQueue();11     }12     //通过这个函数获取当前线程创建的Looper对象,前面说过了一个线程只能有一个Looper对象,所以该Looper对象就是上面创建的那个。13     public static Looper myLooper() {14         return sThreadLocal.get();15     }16 17 18 //Looper跟Handler是怎样建立关系的?19 20 Handler.java21     public Handler() {22         //得到当前线程的Looper对象23         mLooper = Looper.myLooper();24         //得到Looper对象的MessageQueue对象的引用25         mQueue = mLooper.mQueue;26     }27  }28

通过上面两个构造函数就将Looper,MessageQueue,Handler建立起了关系。

消息的分发:

1 Handler.java 2     public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 3         Message msg = Message.obtain(); 4         msg.what = what; 5         return sendMessageDelayed(msg, delayMillis){ 6          7             MessageQueue queue = mQueue; 8             if (queue != null) { 9                 msg.target = this; //这步很重要:将handler的引用记录在了msg.target中。方便后面Looper处理消息后返还消息给Handler的对象。10                 sent = queue.enqueueMessage(msg, uptimeMillis);11                 12                 return sent;13             }14         }15     }16  MessageQueue.java17     //将新插入的消息放到消息队列的最后18     final boolean enqueueMessage(Message msg, long when) {19         20         Message p = mMessages;21         Message prev = null;22         while (p != null && p.when <= when) {23             prev = p;24             p = p.next;25        }26         msg.next = prev.next;27         prev.next = msg;28     29     }30 Looper.java31     //一个死循环32     public static void loop() {33         //得到当前线程的Looper对象34         Looper me = myLooper();35         //得到当前线程的消息队列36         MessageQueue queue = me.mQueue;37         while (true) {38             Message msg = queue.next(); // might block39             if (msg != null) {40                 //将消息返还给Handler的对象,调用我们覆盖的函数41                 msg.target.dispatchMessage(msg);42                 //释放消息队列的中消息43                 msg.recycle();44             45             }46         }47     48     }

从建立关系,到分发消息,最后返还消息的过程大概就是这样了。

过程中还设计到了java中管理线程的一个内容

Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这个对象用于保存不同线程中的数据备份,不涉及android内容。

转载于:https://www.cnblogs.com/mogul/archive/2013/03/27/2984709.html

你可能感兴趣的文章
MySQL 忘记 root 密码重置方法
查看>>
排序之归并排序
查看>>
Windows Socket 编程 : 支持多线程(TCP)(环境:VS2010)
查看>>
BZOJ1096: [ZJOI2007]仓库建设(dp+斜率优化)
查看>>
CAS5.0.X 使用经历
查看>>
HDU 2610 (自己完全找不到思路) Sequence one
查看>>
JAVA对存储过程的调用方法(本文源于网络)
查看>>
排序思想
查看>>
linux服务器git pull/push时提示输入账号密码之免除设置
查看>>
Thinking in Java Reading Note(1.对象导论)
查看>>
3087Shuffle'm Up
查看>>
前端开发基础2(css----选择器)
查看>>
[YTU]_2636 ( B3 指向基类的指针访问派生类的成员函数)
查看>>
第一阶段冲刺8
查看>>
程序猿/媛段子
查看>>
材料的构成 —— 塑料
查看>>
Boltzmann 玻尔兹曼机(BM)
查看>>
前端开发中的字符编码
查看>>
关于是否走索引的讨论
查看>>
Flink ADD Multi Source
查看>>