zoukankan      html  css  js  c++  java
  • Message,MessageQueue,Handler,Looper,Thread,ThreadLocal

    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发送Message
    16                    1:Message被塞入MessageQueue
    17                    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               @Override
    16               public void run() {
    17                 //在当前线程中生成一个新的Looper对象
    18                 Looper.prepare();
    19                 
    20                 //给MessageQueue队列插入一条消息。
    21                 mHandler.sendEmptyMessage(0);
    22                 
    23                 //不断的轮询遍历MessageQueue的消息,并且发送给mHandler
    24                 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.java
    21     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.java
    17     //将新插入的消息放到消息队列的最后
    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.java
    31     //一个死循环
    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 block
    39             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内容。

  • 相关阅读:
    chromedriver安装与配置(ubuntu linux下)
    Ajax 通信技术--hidden Frame GET 请求 和 POST 请求
    photoshop去除图片上的水印
    redis和memcache的对比
    关于mongodb ,redis,memcache之间见不乱理还乱的关系和作用
    MYSQL中'TYPE=MyISAM'错误的解决方案
    http协议
    jQuery常用方法
    SQL Server中行列转换 Pivot UnPivot
    jQuery插件开发
  • 原文地址:https://www.cnblogs.com/mogul/p/2984709.html
Copyright © 2011-2022 走看看