Looper,Handler,Message探究

本文是在阅读张鸿阳的深入理解 Looper、Handler、Message三者关系的一个自我学习总结

Looper

Looper主要作用:

  1. 绑定当前线程,new一个MessageQueue并绑定 ,且保证一个线程只有一个Looper
  2. 不断进行消息轮询,有则分发,无则阻塞

Looper主要是prepare()和loop()两个方法。

  1. prepare()方法
    ** 作用:初始化Looper,保证一个线程初始化一次**
    public static final void prepare() {
    if (sThreadLocal.get() != null) {
    throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(true));
    }
    sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。将一个Looper的实例放入了ThreadLocal,判断sThreadLocal是否为null,否则抛出异常。** 说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例.**

  2. 构造方法Looper()
    ** 作用:绑定当前线程,new一个MessageQueue并绑定**

    private Looper(boolean quitAllowed) {
     mQueue = new MessageQueue(quitAllowed);
     mRun = true;
     mThread = Thread.currentThread();
    }
    

new了一个MessageQueue(消息队列)给全局变量mQueue,获取当前线程赋给全局变量mThread,在prepare()调用了构建方法.

  1. loop()方法
    ** 不断轮询并有消息分发消息 **

    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;
    
     // Make sure the identity of this thread is that of the local process,
     // and keep track of what that identity token actually is.
     Binder.clearCallingIdentity();
     final long ident = Binder.clearCallingIdentity();
    
     for (;;) {
         Message msg = queue.next(); // might block
         if (msg == null) {
             // No message indicates that the message queue is quitting.
             return;
         }
    
         // This must be in a local variable, in case a UI event sets the logger
         Printer logging = me.mLogging;
         if (logging != null) {
             logging.println(">>>>> Dispatching to " + msg.target + " " +
                     msg.callback + ": " + msg.what);
         }
    
         msg.target.dispatchMessage(msg);
    
         if (logging != null) {
             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
         }
    
         // Make sure that during the course of dispatching the
         // identity of the thread wasn't corrupted.
         final long newIdent = Binder.clearCallingIdentity();
         if (ident != newIdent) {
             Log.wtf(TAG, "Thread identity changed from 0x"
                     + Long.toHexString(ident) + " to 0x"
                     + Long.toHexString(newIdent) + " while dispatching to "
                     + msg.target.getClass().getName() + " "
                     + msg.callback + " what=" + msg.what);
         }
    
         msg.recycle();
     }
    }
    
    public static @Nullable Looper myLooper() {    return sThreadLocal.get();}
    

a. 首先在sThreadLocal取一个Looper.
b. 然后不断轮询消息队列,当有消息取出的时候, msg.target.dispatchMessage(msg);msg.targer实际为Handle,那么这个** Handle怎样与Message关联,Message怎么进入消息队列的呢? **
c. 释放msg,msg.recycle();

Handle

  1. 构造方法Handle()**
    作用:获取当前线程中Looper,获取 Looper的Queue赋给全局变量 **
    public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
    final Class<? extends Handler> klass = getClass();
    if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
    (klass.getModifiers() & Modifier.STATIC) == 0) {
    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
    klass.getCanonicalName());
    }
    }

     mLooper = Looper.myLooper();
     if (mLooper == null) {
         throw new RuntimeException(
             "Can't create handler inside thread that has not called Looper.prepare()");
     }
     mQueue = mLooper.mQueue;
     mCallback = callback;
     mAsynchronous = async;
    

    }

  2. 发送消息sendMesssage(Message msg)
    ** 作用:使用全局mQueue发送消息,并且将msg.targer设为this(Handle),解释了Handle如何与MessageQueue,msg关联 **

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
     msg.target = this;
     if (mAsynchronous) {
         msg.setAsynchronous(true);
     }
     return queue.enqueueMessage(msg, uptimeMillis);
     }
    
  3. 接收处理消息

    public void dispatchMessage(Message msg) { 
    if (msg.callback != null) {
    handleCallback(msg);
    }  else {
     if (mCallback != null) { 
    if (mCallback.handleMessage(msg)) { return; } 
     } 
    handleMessage(msg); 
    }
    }
    

覆写Handle的handleMessage就能处理消息了.

Message,Messenger

Message作为传递中的数据载体.使用Message#obtian()进行构建,obj用Bundle传递序列化参数.
Messenger相当于Handle的封装,用于多进程间通信.

后记

面试上我有写这个,面试的时候老喜欢让我讲讲Handle,笔试碰到几率也高。我一般三句话就完了:
1.Looper轮询器,不断轮询,有消息取出,没则阻塞。
2.每个线程只有一个轮询器,消息队列。
3.message是消息载体。
这答案我自己听了都想哭。
所以,我回来再看一般,然后整理一个答案:
1.初始化Handle实例的时候获取当前线程的Looper轮询器。
2.将Looper中的消息队列赋值给Handle中对象中全局变量。
3.Handle利用消息队列发送消息,消息发的targer设置成自己。所以能够跨线程通信。
4.Looper的parper方法保证每个线程只初始化一次,构造方法绑定当前线程,new出消息队列。loop方法不断轮询消息,有读取,无阻塞。
还有会让你说说线程间通信,Handle是要讲的,用在主线程与子线程通信,因为子线程无法更新ui.
但是一般线程间的通信其实是共享同进程内存,读取共享的内存变量(大家都能访问到,比如全局变量,普通类的static变量)就能实现简单的通信了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容