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

PS:因为感冒缘由,本篇写的有点没有主干,你们凑合看吧。。异步

 

学习内容:ide

1.MessageQueue,Looper,MessageQueue的做用.oop

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

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

 

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

1.MessageQueuethis

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

2.Looper线程

  Looper的主要做用是与当前线程造成一种绑定的关系,同时建立一个MessageQueue,这样保证一个线程只能持有一个Looper和MessageQueue同时Looper使得MessageQueue循环起来,就比如水车和水同样,MessageQueue比如水车,当他有了Looper的时候,那么水车会随着水去转动也就是说Looper为MessageQueue提供了活力,使其循环起来,循环的动力每每就少不了Thread。通常而言子线程通常是没有MessageQueue,所以为了使线程和消息队列可以关联那么就须要有Looper来完成了,所以咱们能够这样去实现3d

   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下载

相关文章
相关标签/搜索