【学习笔记】Looper 与 Handler 的关系

什么是异步消息处理线程?异步消息处理过程中有哪些角色?这些角色是怎样关联到一起,完美地完成消息通信的?带你走进 Handler与Looper的世界。

  • 什么是异步消息处理线程

异步消息处理线程启动会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中会取出一个消息,然后回调给相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。

  • 这个消息处理过程有哪些角色

    1. 无限的循环体
    2. 内部的消息队列
    3. 相应的消息处理函数

Looper

对于 Looper这个角色,主要是 prepare() 和 loop( )两个方法。

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 实例的保存。

在 prepare() 方法中,首先进行了 Looper 对象的判空处理,如果不为空(之前已经有了一个 Looper 实例),就会抛出异常,说明了 prepare 不能被调用两次,同时也保证了 一个线程里面只能有一个 Looper 实例。

new Looper(true)里面做了什么呢?

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

在 Looper 的构造方法中,有新增一个 MessageQuene 实例,这个是一个消息队列。

然后看看第二个主要方法 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();    
        }    
}    

还有一个 myLooper() 方法

public static Looper myLooper() {
    return sThreadLocal.get();
}

从 threadLocal 里面去的 looper 实例,如果为空则抛出异常,所以 loop() 方法的调用一定要在 prepare() 之后。

后续的大概意思就是 异步消息处理线程的流程:进入一个无限的循环体for(;;)之中,每循环一次,从其内部的消息队列中final MessageQueue queue = me.mQueue;会取出一个消息Message msg = queue.next();,然后回调给相应的消息处理函数msg.target.dispatchMessage(msg);,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。

Looper的主要作用就是:

  • prepar() 与当前线程绑定,保证一个线程只有一个 Looper 实例,同时一个 Looper实例也只有一个 MessageQuene
  • loop() 消息循环处理机制

理清一下异步消息处理流程的角色 出场了哪些?

  1. 无限的循环体
  2. 内部的消息队列
  3. 相应的消息处理函数

前两个都清楚来路,还剩一个 msg.target 从哪里来?要到哪里去?贫僧从东土大唐而来,去往西天拜佛求经

于是:Handler 登场。

一般 Activity 中使用 Hander 有两种方式

####先说第一种handler方式:

private Handler mHandler = new Handler()    
    {    
        public void handleMessage(android.os.Message msg)    
        {    
            switch (msg.what)    
            {    
            case value:    
                    
                break;    
    
            default:    
                break;    
            }    
        };    
    };    

需要看两个方法:Handler() 的构造函数干了什么?handleMessage 是谁通过什么方式调用的?

  • Handler 是怎么与 消息队列(MessageQueue)联系上的?他在子线程发送的消息是怎么发送到 消息队列(MessageQueue)的?
public Handler() {    
        this(null, false);    
}    
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;    
    }    

先通过 Looper.myLooper 获得 当前线程的 Looper 实例如果没有,则无法创建 Handler

在 非UI线程 创建 Handler 的时候 通常都会 抛出异常:Can’t create handler inside thread that has not called Looper.prepare()。

这里有两个知识点:

  1. UI 线程 (Activity)并没有显式地调用 Looper.prepare() 和 Looper.loop() 方法,是因为在 启动代码中,已经在当前UI线程中调用的 这两个方法,所以直接在 UI线程创建 Handler 时 Looper.myLooper()是会返回当前线程(UI线程)的唯一 Looper实例的。
  2. 在 非UI 线程中创建 Handler 时,应该要进行 Looper方法的显式调用

通过得到的 Looper 实例,进一步获取 Looper 的 MessageQueue(消息队列),这样Handler 就得到了 MessageQueue 的联系方式。接下来就是 联系MessageQueue 的具体做法了打电话还是wechat?

public final boolean sendMessage(Message msg)    
 {    
     return sendMessageDelayed(msg, 0);    
 } 
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {    
     Message msg = Message.obtain();    
     msg.what = what;    
     return sendMessageDelayed(msg, delayMillis);    
 }   
public final boolean sendMessageDelayed(Message msg, long delayMillis)    
   {    
       if (delayMillis < 0) {    
           delayMillis = 0;    
       }    
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    
   }    
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    
       MessageQueue queue = mQueue;    
       if (queue == null) {    
           RuntimeException e = new RuntimeException(    
                   this + " sendMessageAtTime() called with no mQueue");    
           Log.w("Looper", e.getMessage(), e);    
           return false;    
       }    
       return enqueueMessage(queue, msg, uptimeMillis);    
   }   

终于走到了能看到MessageQueue的地方 sendMessageAtTime()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
       msg.target = this;    
       if (mAsynchronous) {    
           msg.setAsynchronous(true);    
       }    
       return queue.enqueueMessage(msg, uptimeMillis);    
   }

msg.target 很眼熟,不玩初心才行,这上面一大串转接就是 为了找到 msg.target 的来路。

终于找到了,this!!!是谁?~~这个属于java的基础知识

public static void loop() {    
        ...  
        for (;;) {    
            ...
            msg.target.dispatchMessage(msg);    
            ...  
        }    
}    

这句就是在做 消息处理 这件事情,至此,案情水落石出,真相只有一个犯人target 就是 handler

wait a second. 还有一个疑点。 handler处理的消息是从哪里来的?

queue.enqueueMessage(msg, uptimeMillis); 

这么明显,handler 发送的消息被送到 MessageQueue 消息队列里面的,然后交给 target 的 dispatchMessage 去处理了。看一看 dispatchMessage

public void dispatchMessage(Message msg) {    
        if (msg.callback != null) {    
            handleCallback(msg);    
        } else {    
            if (mCallback != null) {    
                if (mCallback.handleMessage(msg)) {    
                    return;    
                }    
            }    
            handleMessage(msg);    
        }    
    }    
/**  
   * Subclasses must implement this to receive messages.  
   */    
  public void handleMessage(Message msg) {   
    
  }   

这是个空方法。

msg.callback 是什么?我发送的消息只有 msg 跟 what ,并没有callback啊,所以一般都是 null 的,else 必然会调用的方法是 handleMessage(msg),这个也很眼熟,不就是 new Handler() 之后需要重写的 消息处理函数吗?

到这里就比较明白了。

总结陈词:

  1. Looper 会在 当前线程中 保存唯一一个 Looper 实例,然后唯一的Looper 实例会在 Looper.prepare() 方法新建一个 同样在一个线程里唯一的 MessageQueue消息队列。
  2. Looper.loop() 会让当前线程进入一个无限循环,然后不断地从MessageQueue 的实例中读取消息,然后回调 msg.target.dispatchMessage(msg)方法。
  3. Handler 构造方法中通过当前线程的 Looper 实例,从而得到 MessageQueue消息队列的联系方式
  4. Handler 通过 sendMessage 方法 发送消息 Message 给 MessageQueue,并且在 Message 中标明自己的身份 msg.target = this, 从而在联系MessageQueue 的过程中暴露自己的身份,完成后续的消息处理。
  5. 构造 Handler 实例时,重写 handleMessage 方法,也就是 msg.targte.dispatchMessage 最终调用的方法。
  6. 至此,整个 Looper 与 handler 的 联系过程完成。

####再说第二种handler方式

mHandler.post(new Runnable()    
        {    
            @Override    
            public void run()    
            {    
                Log.e("TAG", Thread.currentThread().getName());    
                tv_text.setText("message received.");    
            }    
        });    

需要看两个方法 handler.post 干了什么? 谁调用了 run 方法?

public final boolean post(Runnable r)    
   {    
      return  sendMessageDelayed(getPostMessage(r), 0);    
   }    
private static Message getPostMessage(Runnable r) {    
      Message m = Message.obtain();    
      m.callback = r;    
      return m;    
  }   

在上面方法中,看到了 message 的 callback 被赋值了,之前在 msg.target.dispatchMessage() 方法中有见到。

Tips: 产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存

public final boolean sendMessageDelayed(Message msg, long delayMillis)    
   {    
       if (delayMillis < 0) {    
           delayMillis = 0;    
       }    
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    
   }    
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    
       MessageQueue queue = mQueue;    
       if (queue == null) {    
           RuntimeException e = new RuntimeException(    
                   this + " sendMessageAtTime() called with no mQueue");    
           Log.w("Looper", e.getMessage(), e);    
           return false;    
       }    
       return enqueueMessage(queue, msg, uptimeMillis);    
   }    

又回到了这里 sendMessageAtTime() 跟 handler.sendMessage 殊途同归。

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

不过,这次的 callback 可不为null 了,所以会走 handlerCallbace(msg) 分支

    private static void handleCallback(Message message) {
        message.callback.run();
    }

也就是调用 Runnable 的 run 方法。

关于 Looper , Handler, Message 的关系已经很清楚了。

Handler 不仅可以更新UI, 我们完全可以在一个子线程中去创建一个 Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在创建 Handler 实例的线程中执行。

####想到一个问题

  • 一个 Activity 可以有多个 handler吗*
 
comments powered by Disqus