Android之消息机制Handler,Looper,Message解析

PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧。。

 

学习内容:

1.MessageQueue,Looper,MessageQueue的作用.

2.子线程向主线程中发送消息

3.主线程向子线程中发送消息

 

  异步消息处理机制是Android提供给我们异步更新UI的一种很好的方式,线程之间以Handler作为桥梁,使得Message可以在线程间进行传递,从而实现异步的一种方式。

1.MessageQueue

  MessageQueue顾名思义,指的就是消息队列,说这个之前我们首先需要知道什么是Message,比如说我们在UI界面时点击一个按钮,或者是接收到了一条广播,其实都算是一条Message,这些事件都被封装成一条Message被添加到了MessageQueue队列当中,因为我们知道一个线程在一段时间只能对一种操作进行相关的处理,因此这些消息的处理就要有先后顺序,因此采用MessageQueue来管理,也就是消息队列。消息队列其实就是一堆需要处理的Message而形成的传送带。一旦有消息发送进来,那么直接执行enqueueMessage()方法。也就是将消息压入到队列当中,一旦线程空闲下来,那么直接从MessageQueue中取出消息,使得消息出队。

2.Looper

  Looper的主要作用是与当前线程形成一种绑定的关系,同时创建一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue同时Looper使得MessageQueue循环起来,就好比水车和水一样,MessageQueue好比水车,当他有了Looper的时候,那么水车会随着水去转动也就是说Looper为MessageQueue提供了活力,使其循环起来,循环的动力往往就少不了Thread。一般而言子线程一般是没有MessageQueue,因此为了使线程和消息队列能够关联那么就需要有Looper来完成了,因此我们可以这样去实现

   class ChildRunnable implements Runnable{

        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Log.e("TAG",msg.obj+"");
                }
            };
            Log.e("TAG_2",Looper.myLooper().toString());
            Looper.loop();

        }
    }

  这里调用了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));
}

  方法比较的简单,我们可以看到prepare()方法首先判断了sThreadLocal中持有的线程引用是否为空,如果不为空,那么直接就会抛异常,这也就说明了Looper.prepare()在一个线程中只允许调用一次,这样也同样为一个线程对应一个Looper做了保障。当Looper.prepare()执行完毕之后Looper才可以执行loop()方法。

public static void loop() {
        
        /**
         * 获取当前线程绑定的Looper
         */
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        /**
         * 当前线程的MessageQueue
         */
        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();
        
        /**
         * 死循环,循环从MessageQueue取出消息.
         */
        for (;;) {
            /**
             * 从Queue中取出一条消息
             */
            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();
        }
}

  这里我们可以看到loop()是通过几个流程使得MessageQueue循环起来的,首先通过静态方法获取当前线程对应的Looper,然后获取由Looper创建的MessageQueue,然后进入死循环,阻塞式的从MessageQueue中取出消息,获取到消息之后将消息分发出去,否则进入等待状态,等待新的消息到来。这就是Looper使MessageQueue循环起来的方式。

  那么我们现在MessageQueue消息队列已经存在了,Looper以线程为动力使得MessageQueue循环了起来,那么消息的发送和处理由谁来完成呢显而易见这就是Handler的作用了.

3.Handler

  Handler的出现使得消息可以被发送和接收,Handler的构造方法有多个。

public Handler() {
       
}

public Handler(Handler.Callback callback) {
       
}

public Handler(Looper looper) {
      
}

public Handler(Looper looper, Handler.Callback callback) {
        
}

  这是Handler的四个构造方法,1和2都没有传递Looper,那么Handler通过调用Looper.myLooper()方法将Handler与Looper和MessageQueue形成绑定关系,而3和4直接传递Looper,那么Handler将直接将传递进来的Looper对象进行保存,直接和传递的Looper以及相关的MessageQueue形成绑定关系。同时这四个构造方法还有不同点,就是是否传递Callback回调接口对应的参数。

public interface Callback {
    public boolean handleMessage(Message msg);
}

  实现Callback接口是实现处理Message的一种方式,而另一种方式则不传递Callback回调接口,而是直接实现Handler中handleMessage方法,也就是说我们可以通过这两种方式实现Handler对消息的处理。这样三者就形成了绑定关系,然后我们来看看Handler的sendMessage等方法.其实无论是调用了哪种方法,sendMessage(),sendMessageDelayed()等等,最后都会调用sendMessageAtTime()方法。这里需要先说一下post方法和sendMessage方法。一般我们使用Handler发送消息的时候也会这样去写。

 handler.post(new Runnable() {
    @Override
    public void run() {
        /**
           * 相关操作
           * */
    }
});

  这样发送消息也是可以的,post执行过程是这样的。

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

  post方法也是发送了一条消息,runnable则作为callback参数作为回调,用于后续处理消息。这里也调用了sendMessageDelayed()方法,最后还是会调用sendMessageAtTime()方法,因此可以看出,无论是send还是post,最后都会调用sendMessageAtTime()方法。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
   MessageQueue queue = mQueue;  
   return enqueueMessage(queue, msg, uptimeMillis);  
}  

  sendMessageAtTime()是最终调用的方法,他通过调用enqueueMessage将消息加入到消息队列当中。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //注意下面这行代码
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //注意下面这行代码
        return queue.enqueueMessage(msg, uptimeMillis);
}

  enqueueMessage方法比较的简单,这里讲msg.target = this,将msg的target赋值为当前的handler,然后调用queue.enqueueMessage()方法将消息加入到消息队列当中。那么SendMessage之后,Looper就通过loop()方法不断的从MessageQueue取出消息,然后通过dispatchMessage()方法将消息分发给Handler,交给Handler去处理相关的Message.

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

  这样我们就可以直接去处理相关的消息了,这个方法做了一些判断,也就是判断是否传递了callback属性,如果msg.callback不为空,那么则表示是post发送过来的消息,在处理的时候就需要调用handleCallback(msg)方法,然后我们会继续判断Handler的Callback是否为空,也就是我们在new Handler(Handler.Callback)方法去创建Handler的时候的判断,如果不为空,那么执行我们Callback中实现的handleMessage()方法,如果为空,那么就直接执行Handler中的handleMessage()方法。总体的流程就是这样。在其他的牛人博客中,看到了一幅特别形象的流程图。在这里贴出来。

  在现实生活的生产生活中,存在着各种各样的传送带,传送带上面洒满了各种货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另一端进行收集处理。

  我们可以把传送带上的货物看做是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,我们可以把发送机滚轮看做是Looper,而发动机的转动是需要电源的,我们可以把电源看做是线程Thread,所有的消息循环的一切操作都是基于某个线程的。一切准备就绪,我们只需要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当我们按下开关的时候,我们就相当于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。

  那Hanlder在传送带模型中相当于什么呢?我们可以将Handler看做是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另一端顺着管道划出传送带。我们在传送带的一端放入货物的操作就相当于我们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另一端顺着管道划出时,我们就相当于调用了Hanlder的dispatchMessage方法,在该方法中我们完成对Message的处理。

2.子线程向主线程发送消息

  子线程向主线程发送消息,平时还是很常用的,比如说子线程完成了一个耗时的操作,需要主线程去更新UI,那么这个时候就需要子线程向主线程中发送相关的消息。用起来也比较的简单。

private Handler handler = new Handler(){
         //主线程做处理.
        @Override
        public void handleMessage(Message msg) {
            String data = (String) msg.obj;
            textView.setText("从子线程发送过来的消息"+data);
            childRunnable = null;
        }
};
class ChildRunnable implements Runnable {
        //子线程发送相关消息
        @Override
        public void run() {
            Message message = Message.obtain();
            message.obj = "ChildToMain";
            handler.sendMessage(message);
        }
}

 3.主线程将消息发送给子线程

  我们经常将消息从子线程发送给主线程,其实主线程也是可以发消息给子线程的。在默认情况下子线程是没有Looper和MessageQueue的,因此我们需要为子线程创建一个Looper然后与子线程的Handler形成绑定关系。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_main_to_child);
        Log.e("TAG_0",Looper.getMainLooper().toString());
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Message message = Message.obtain();
                message.obj = "MainToChild";
                handler.sendMessage(message);
            }
        });
        new Thread(new ChildRunnable()).start();

    }

    class ChildRunnable implements Runnable{

        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Log.e("TAG",msg.obj+"");
                }
            };
            Log.e("TAG_2",Looper.myLooper().toString());
            Looper.loop();

        }
    }

  这里设置了一个按钮的监听事件来控制主线程向子线程发送消息,或者我们先让主线程休眠,先让子线程初始化完毕之后,主线程再向子线程发送消息,如果直接发送的话,很有可能在Handler还没有初始化完毕后就导致消息发送,这样就会出现Handler为null从而导致NullPointerExpection发生。因此在这里进行了简单的控制,这里我们可以看到,如果子线程想拥有自己的Looper和MessageQueue首先需要执行Looper.prepare()和Looper.loop()方法。才能为子线程的Handler绑定上Looper和MessageQueue。有人可能会问道,为什么主线程直接发送消息就可以,而不需要调用这两个方法,这是因为Activity在onCreate的时候已经执行了这些方法,因此我们可以直接发消息给主线程。

 因此当我们在使用Handler Message Looper实现异步消息机制的时候,如果想实现多级通信,那么就需要弄明白当前的Looper MessageQueue绑定的是哪个Handler。Looper,MessageQueue在一个线程中只有唯一一个,但是Handler可以是多个的,我们只需要控制这个绑定关系是实现多级通信的关键。

 最后贴一个简单的源代码:Demo下载

 

 

 

  

 

posted @ 2016-12-12 14:49  代码丶如风  阅读(9114)  评论(1编辑  收藏  举报