看完这篇,你就懂了Android的消息机制,整理思路,让我们开始吧

来源:互联网 发布:c语言还是p语言好 编辑:程序博客网 时间:2024/05/15 14:40

        如果,要学Android的,那么就一定会接触到Android的消息机制,那么学Android的消息机制就一定会涉及到Handler、Message、MessageQueue、Looper等概念。

       关于这篇文章的相关内容,其他大神已经写过很多遍。鄙人也是在多次查看他人的文章+源代码+总结后决定写这篇文章。自己写的话,一是为了总结,方便日后查看,二是本人能力有限,有不对的地方还是希望大家多多指正。

       Handler:什么是Handler呢,只要打开Handler类源码,类注释就能清晰的告诉我们,什么是Handler。Handler允许我们发送和处理存放在与之相关联线程的MessageQueue(消息队列)中的Message对象或者Runnable对象。

       当我们创建Handler时,Handler会和当前线程的消息队列绑定,Handler可以传递messages或者runnables到消息队列中,同时可以从消息队列取出并执行。那么Handler的作用是干嘛呢?Handler主要有两个作用:一是在某个时间计划执行messages或runnables,二是:在不同线程执行操作(例如子线程做耗时操作后,在主线程更新UI)。

        Looper:Looper持有并管理MessageQueue,循环取出messages直到为空,当MessageQueue没有message时,会阻塞。创建线程时默认是没有message loop,所以子线程处理消息会发生异常,可以通过调用Looper.prepare()创建,和调用Looper.loop()方法循环处理消息直到停止。而在Android创建主线程时,默认会创建MainLooper,可以通过Looper.getMainLooper获得。

class LooperThread extends Thread {        public Handler mHandler;          public void run() {            Looper.prepare();//子线程创建Handler的正确姿势。            mHandler = new Handler() {                public void handleMessage(Message msg) {                   // process incoming messages here               }           };             Looper.loop();       }

      Message:消息,用来干嘛?用来携带用户数据,由Handler发送和处理,可以不同线程之间传递。

      MessageQueue:消息队列,用于存放Message的地方。

那么,假设我们要在子线程做耗时操作,在主线程操作UI界面。那么代码应该如下:

首先,在主线程创建Handler,由于主线程默认会创建message loop,需要我们去调用Looper.preapre()和Looper.loop()方法。所以代码如下:

Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {            @Override            public boolean handleMessage(Message msg) {                //接受到消息,在主线程处理相关操作:更新UI等                return false;            }        });

       而在子线程,做一些耗时操作后,发送消息,代码如下:

 Thread thread = new Thread(new Runnable() {            @Override            public void run() {                Message message = handler.obtainMessage();                Bundle bundle = new Bundle();                bundle.putInt("int", 9);                message.setData(bundle);                handler.sendMessage(message);            }        });        thread.start();

        在子线程处理完相关耗时操作后,通过handler.obtainMessage()方法从消息池中获得message对象,通过bundle设置要携带的数据,并调用handler的sendMessage发送message。随后,在主线程,handler就会回调handler的handleMessage方法进行相关的处理进行相关的处理。

       Handler类中与sendMessage方法有很多类似的方法,下面列举几个:

               post(Runnable r)发送runnable对象   

               postAtTime(Runnable r, long uptimeMillis)在某个时间点发送runnable对象

postDelayed(Runnable r, long delayMillis)延迟多长时间发送runabled对象

       其实以上post的runnab对象最终都会被封装到message的callback。然后调用对应的sendMessage(Message msg)、sendMessageAtTime(Message msg, long uptimeMillis)、

sendMessageDelayed(Message msg, long delayMillis)。查看一下post(Runnable r)的源码便知:

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对象也可以通过Message的obtain方法从消息池中获得。也可以通过new一个message对象,但不建议,官网建议通过Handler或Message的obtain方法从消息池获得,因为会服用Message对象,减少不必要开销。

       Handler无论以哪种方式发送messages或者runnables,最终会调用Handler的sendMessageAtTime(Message msg, long uptimeMillis) 。查看一下源码,可以知道是将message插入到队列的最后一个,等待被执行(可以忽略一下源码):

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

       通过源码可以知道,当消息队列为null时会抛出异常,这也就是为什么我们要在子线程需要先调用Looper.prepare()方法,创建消息队列的。我们再来看看enqueueMessage方法。

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

        由代码可以知道,message中的target保存着当前handler对象,接着调用消息队列queue的enqueueMessage方法。那么我们查看一下MessageQueue的对应该方法。

boolean enqueueMessage(Message msg, long when) {        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

        通过方法的调用返回值可以知道,当返回为true时,表示插入message插入到Message Queue正常,反之异常。

        那么,message存入到Message Queue后,我们怎么在主线程接收该message?通过前面介绍,我们知道Looper持有MessageQueue并管理Message,通过调用Looper.loop()死循环得从Message Queue取出message,处理message,直到Message Queue没有message后,处于阻塞状态,查看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            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            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();        }    }
        通过绿色那一行代码可知道,当Message Queue中还有Message的话,会调用对应的Handler的dispatchMessage(Message msg),没有的话,处于阻塞状态;那么,我们查看一下Handler中handlerMessage方法做了什么:

 public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);//优先处理msg的callback对象,也就是我们post(Runable r)        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);//调用我们在创建Handler的回调方法        }    }
        通过该方法,我们也就知道为什么,最终我们能在主线程更新UI了。
        通过一张图来,看整体的流程:



如果您觉得对你有帮助,就点个赞吧。


阅读全文
0 0
原创粉丝点击