美文网首页程序员
Handler需要知道的知识点

Handler需要知道的知识点

作者: amazingokc | 来源:发表于2017-03-14 17:56 被阅读188次

1.Handler存在的意义

对于我们而言主要是用于刷新UI,因为子线程不能刷新UI,如果你对handler有充分的了解,还是可以用到其他地方的,鸿洋大神有篇博客是用handler加载图片的,不妨去研究一番。

2.掌握Handler一些必备知识点

要想实现子线程通知Handler刷新UI是比较简单的,你只要看下别人的写法自己就能模仿写出来,写多几次就能无脑的写出来了,我以前就是这么的无脑操作使用Handler刷新UI的。但是,知耻而后勇的我良心发现,这么无脑不行啊,决定去看下各位大神对Handler的讲解,总算有一番了解。
不扯淡,Handler、Looper、MessageQueue三个是一起工作的,以我的表达能力根本不用画图就能讲清他们之间的关系,先从三个单词了解它们,Handler是处理者的意思,MessageQueue是消息队列的意思,Looper循环的意思,他们之间的关系很简单,Handler有几个方法可以发送信息(当我们的子线程需要通知刷新UI的时候就这么做)到MessageQueue,Looper的loop()方法一直循环读取MessageQueue里面的消息,然后将消息分发给对应的Handler,一般是把信息交给Handler的HandlerMessage()方法,所以我们在HandlerMessage()处理消息(比如拿到的数据显示在对应的view上面),这是一个大概的流程,代码展示大概是如下几步:

3.从源码才能更加了解更多的细节(涉及到Handler、Looper、MessageQueue、Message的源码)

  • 1.在UI线程中创建两个Handler(我们这里讲的Handler、Looper、MessageQueue都是属于UI线程的)

    private Handler mHandler1 = new Handler() {
    
      @Override
      public void handleMessage(Message msg) {
          super.handleMessage(msg);
          LLog.d("handleMessage", "" + msg.what);
      }
    } ;
    
    private Handler mHandler2 = new Handler() {
    
      @Override
      public void handleMessage(Message msg) {
          super.handleMessage(msg);
          LLog.d("handleMessage", "" + msg.what);
      }
    };
    
  • 2.在子线程中使用mHandler1和mHandler2发送消息到同一个MessageQueue(主线程始终只有一个)

     new Thread() {
    
          @Override
          public void run() {
              super.run();
              //在子线程中使用UI线程的Handler将想要发送的信息发送到MessageQueue中
              //Looper会一直从MessageQueue拿到信息交给handleMessage(),所以我们只需要在handleMessage()
              //对消息进行处理即可,其实主线程只有一个MessageQueue
              mHandler1.sendEmptyMessage(1);
    
              mHandler2.sendEmptyMessage(2);
          }
      }.start();
    

为什么sendEmptyMessage()之后,消息就来到了handleMessage(Message msg)呢?
因为newHandler()和sendEmptyMessage()方法隐蔽了很多的细节,所以我们需要打开Handler的源码才能知道流程,请看源码:

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();      //拿到当前线程(UI线程)的Looper()实例
    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实例,前面说过需要Looper的loop()方法不断的从MessageQueue中不断获取Message(消息),所以必须要有一个Looper实例,不急着看Handler的其他源码,先点进这里Looper.myLooper()看下是如何获取到Looper实例的。

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();   
}

Looper源码这里是拿到了当前线程的Looper实例,为什么一get就能有呢?因为主线程创建的时候已经帮我们set进去了,所以我们不用收到set进去,到底是如何set进去的?这是后就需要灵性了在当前类搜索找到sThreadLocal.set,是的,找到了:

 /** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

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

最后一行看到了吧,简单粗暴直接new了一个Looper()进去,外层方法是prepare(),方法名是准备的意思,看来就是调用这个准备方法的时候就创建了一个Looper实例set进去了,Looper的构造函数里面直接创建了一个MessageQueue,MessageQueue(消息队列)是用于存放Message(消息)的,此时三个基佬都有了各自的实例了,那么就可以合作干活了。
如果你对sThreadLocal感兴趣,其实他就是一个static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();不懂自行学习,我也不太懂先

说了这么多连Looper在哪里创建的都不知道,我TM差点都忘了。对于这个UI线程的Looper实例是不用我们进行创建的,很多关于这部分知识的讲解都有讲到,Android 在进程的入口函数 ActivityThread.main()中,调用 Looper.prepareMainLooper, 为应用的主线程创建Looper,源码:
public final class ActivityThread {

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      if (activity != null) {
           activity.attach(appContext, this, ..., );

public static void main(String[] args) {
    // 在这儿调用 Looper.prepareMainLooper, 为应用的主线程创建Looper
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    Looper.loop();
  }
}

看到注释那里,Looper.prepareMainLooper()这里创建了主线程Looper,而不是调用Looper.prepare()方法创建,其实Looper.prepareMainLooper()调用了Looper.prepare(false),最终还是调用了,看到这里的最后一行
Looper.loop()这个方法点击进去看是一直获取MessageQueue的Message的,看源码见分晓(略长,跟着注释看):

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();    //这个方法前面讲过,就是Looper 实例,如果你忘了这个方法很正常,看多几次就OK
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;   //这个是消息队列,等下就要掏空它(从里面不断获取Message)

    // 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.recycleUnchecked();
    }
}

以上逻辑相当的简单,只要把msg.target.dispatchMessage(msg)这个函数流程走完就OK了,target是啥?为什么它有dispatchMessage(msg)方法,灵性又发挥作用了,msg.target.dispatchMessage(msg)这里面的msg是从消息队列拿到的消息,我们知道消息最后是在Handler的方法里进行处理的,那target应该就是Handler实例了,马上去Message源码搜索target,是的,我们发现他就是Handler,源码:Handler target;,这时我们需要知道什么时候将这个target赋值的,回到最开始我们new 出一个Handler的那里,只有在那里我们才操作了Handler,我们调用了handler.sendEmptyMessage();,看这里的源码,一直跟踪下去:

/**
 * Sends a Message containing only the what value.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

/**
 * Sends a Message containing only the what value, to be delivered
 * after the specified amount of time elapses.
 * @see #sendMessageDelayed(android.os.Message, long) 
 * 
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

/**
 * Sends a Message containing only the what value, to be delivered 
 * at a specific time.
 * @see #sendMessageAtTime(android.os.Message, long)
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

 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);
}

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

}

跟了大半天才跟踪到这行msg.target = this; 是的,我们在这里将Handler放到了Message的targe字段里面了,发了一条消息过来肯定要做个标志嘛,不然这么多消息扔到消息队列,取出来都不知道给回谁,所以我们在条消息对象标记好各自的Handler,最后取出来才能给到对应的Handler。好的,知道了targe是Handler,那么msg.target.dispatchMessage(msg);的dispatchMessage(msg)方法就是在Handler里面了,终于把消息给回老子(Handler)了.源码源码源码,看下:

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

直接看else里面的,if里面的的情况需要另外分析,我们发送的这些消息会交给else处理就是了,handleMessage(msg)等你等到想分手了,经历了这么多,让我更见的了解你,以后再也不会找这么久了。
记得前面是这么写的

mHandler1.sendEmptyMessage(1);

mHandler2.sendEmptyMessage(2);

那么很自然的,下面一个输出1,一个输出2

private Handler mHandler1 = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        LLog.d("handleMessage", "" + msg.what);
    }
} ;

private Handler mHandler2 = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        LLog.d("handleMessage", "" + msg.what);
    }
};

第一次敲博客,如果哪里讲错或者思路有些逆天,麻烦指点下,如果你是刚接触Handler,很有可能需要多看几次才能理解。如果你完全理解了,就在子线程写个Handler,在子线程发送信息,让Handler处理信息吧。

4总结一下:以后坚持写博客,真的有用。

相关文章

网友评论

    本文标题:Handler需要知道的知识点

    本文链接:https://www.haomeiwen.com/subject/cdpfnttx.html