美文网首页
Android消息处理机制(Handler、Looper、Mes

Android消息处理机制(Handler、Looper、Mes

作者: 程序猿BOSS | 来源:发表于2017-10-24 14:27 被阅读37次

    Android是消息驱动的,实现消息驱动有几个要素:

    1、Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理

    2、处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message

    msg)方法来对特定的Message进行处理,例如更新UI等。

    3、MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

    4、Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

    5、Thread:线程,负责调度整个消息循环,即消息循环的执行场所。获取loop

    Looper.loop();

    Public staticvoidloop(){

    finalLooperme=myLooper();

    }

    //返回和线程相关的looper

    Public staticLoopermyLooper(){

    returnsThreadLocal.get();

    }

    Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该

    线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。 如下例所示:

    class LooperThread

    extends Thread {

    public Handler mHandler;

    public void run() {

    Looper.prepare();

    mHandler = new Handler() {

    public void handleMessage(Messagemsg) {

    // process incoming messageshere

    }

    };

    Looper.loop();

    }

    }

    这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。

    Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件

    Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的

    public final class

    ActivityThread {

    ......

    public

    static final void main(String[] args) {

    ......

    Looper.prepareMainLooper();

    ......

    ActivityThread

    thread = new ActivityThread();

    thread.attach(false);

    ......

    Looper.loop();

    ......

    thread.detach();

    ......

    }

    }

    这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中

    初始化消息队列

    class LooperThread

    extends Thread {

    public Handler mHandler;

    public void run() {

    Looper.prepare();

    mHandler = new Handler() {

    public void handleMessage(Messagemsg) {

    // process incoming messageshere

    }

    };

    Looper.loop();

    }

    }

    主要是红色标明的两句,首先调用prepare初始化MessageQueue与Looper,然后调用loop进入消息循环。先看一下Looper.prepare。

    public static void

    prepare() {

    prepare(true);

    }

    private static void

    prepare(boolean quitAllowed) {

    if (sThreadLocal.get() != null) {

    throw new RuntimeException("Onlyone Looper may be created per thread");

    }

    sThreadLocal.set(new Looper(quitAllowed));

    }

    重载函数,quitAllowed默认为true,从名字可以看出来就是消息循环是否可以退出,默认是可退出的,Main线程(UI线程)初始化消息循环时会调用prepareMainLooper,传进去的是false。使用了ThreadLocal,每个线程可以初始化一个Looper。

    private Looper(boolean quitAllowed) {

    mQueue = new MessageQueue(quitAllowed);

    mThread = Thread.currentThread();

    }

    MessageQueue(boolean quitAllowed) {

    mQuitAllowed = quitAllowed;

    mPtr = nativeInit();//mPtr记录native消息队列的信息

    }

    在Looper初始化时,新建了一个MessageQueue的对象保存了在成员mQueue中。MessageQueue的构造函数是包可见性,所以我们是无法直接使用的,在MessageQueue初始化的时候调用了nativeInit,这是一个Native方法:

    android_os_MessageQueue_nativeInit();

    static jlong

    android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {

    NativeMessageQueue*nativeMessageQueue = new NativeMessageQueue();//初始化native消息队列

    if (!nativeMessageQueue) {

    jniThrowRuntimeException(env,"Unable to allocate native queue");

    return 0;

    }

    nativeMessageQueue->incStrong(env);

    returnreinterpret_cast(nativeMessageQueue);

    }

    在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。

    再看NativeMessageQueue的实现:

    new

    NativeMessageQueue();

    NativeMessageQueue::NativeMessageQueue()

    : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {

    mLooper =Looper::getForThread(); //获取TLS中的Looper对象

    if (mLooper == NULL) {

    mLooper = newLooper(false); //创建native层的Looper

    Looper::setForThread(mLooper); //保存native层的Looper到TLS中

    }

    }

    Looper::getForThread(),功能类比于Java层的Looper.myLooper();

    Looper::setForThread(mLooper),功能类比于Java层的ThreadLocal.set();

    NativeMessageQueue::NativeMessageQueue()

    : mInCallback(false), mExceptionObj(NULL) {

    mLooper = Looper::getForThread();

    if (mLooper == NULL) {

    mLooper = new Looper(false);

    Looper::setForThread(mLooper);

    }

    }

    在NativeMessageQueue的构造函数中获得了一个Native层的Looper对象,Native层的Looper也使用了线程本地存储

    发送消息

    通过Looper.prepare初始化好消息队列后就可以调用Looper.loop进入消息循环了,然后我们就可以向消息队列发送消息,消息循环就会取出消息进行处理,在看消息处理之前,先看一下消息是怎么被添加到消息队列的。

    在Java层,Message类表示一个消息对象,要发送消息首先就要先获得一个消息对象,Message类的构造函数是public的,但是不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的,如下所示:

    public static

    Message obtain() {

    synchronized (sPoolSync) {

    if (sPool != null) {

    Message m = sPool;

    sPool = m.next;

    m.next = null;

    sPoolSize--;

    return m;

    }

    }

    return new Message();

    }

    public void recycle() {

    clearForRecycle();

    synchronized (sPoolSync) {

    if (sPoolSize < MAX_POOL_SIZE) {

    next = sPool;

    sPool = this;

    sPoolSize++;

    }

    }

    }

    Message内部通过next成员实现了一个链表,这样sPool就了为了一个Messages的缓存链表。

    消息对象获取到了怎么发送呢,大家都知道是通过Handler的post、sendMessage等方法,其实这些方法最终都是调用的同一个方法sendMessageAtTime:

    public boolean

    sendMessageAtTime(Message msg, long uptimeMillis) {

    MessageQueue queue = mQueue;

    if (queue == null) {

    RuntimeException e = newRuntimeException(

    this + "sendMessageAtTime() called with no mQueue");

    Log.w("Looper",e.getMessage(), e);

    return false;

    }

    return enqueueMessage(queue, msg,uptimeMillis);

    }

    sendMessageAtTime获取到消息队列然后调用enqueueMessage方法,消息队列mQueue是从与Handler关联的Looper获得的。

    private boolean

    enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

    msg.target = this;

    if (mAsynchronous) {

    msg.setAsynchronous(true);

    }

    return queue.enqueueMessage(msg,uptimeMillis);

    }

    enqueueMessage

    接下来看一下是怎么MessageQueue的enqueueMessage。

    final boolean

    enqueueMessage(Message msg, long when) {

    if (msg.isInUse()) {

    throw newAndroidRuntimeException(msg + " This message is already in use.");

    }

    if (msg.target == null) {

    throw newAndroidRuntimeException("Message must have a target.");

    }

    boolean needWake;

    synchronized (this) {

    if (mQuiting) {

    RuntimeException e = newRuntimeException(

    msg.target + "sending message to a Handler on a dead thread");

    Log.w("MessageQueue",e.getMessage(), e);

    return false;

    }

    msg.when = when;

    Message p = mMessages;

    if (p == null || when == 0 || when< p.when) {

    // New head, wake up the eventqueue if blocked.

    msg.next = p;

    mMessages = msg;

    needWake = mBlocked;

    } else {

    // Inserted within the middleof the queue.Usually we don't have towake

    // up the event queue unlessthere is a barrier at the head of the queue

    // and the message is theearliest asynchronous message in the queue.

    needWake = mBlocked &&p.target == null && msg.isAsynchronous();

    Message prev;

    for (;;) {

    prev = p;

    p = p.next;

    if (p == null || when

    break;

    }

    if (needWake &&p.isAsynchronous()) {

    needWake = false;

    }

    }

    msg.next = p; // invariant: p== prev.next

    prev.next = msg;

    }

    }

    if (needWake) {

    nativeWake(mPtr);

    }

    return true;

    }

    在enqueueMessage中首先判断,如果当前的消息队列为空,或者新添加的消息的执行时间when是0,或者新添加的消息的执行时间比消息队列头的消息的执行时间还早,就把消息添加到消息队列头(消息队列按时间排序),否则就要找到合适的位置将当前消息添加到消息队列。

    消息循环

    消息队列初始化好了,也知道怎么发消息了,下面就是怎么处理消息了,看Handler.loop函数:

    public static void

    loop() {

    final Looper me =myLooper();//从该线程中取出对应的looper对象

    if (me == null) {

    throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");

    }

    final MessageQueue queue = me.mQueue;//取消息队列对象...

    // Make sure the identity of thisthread is that of the local process,

    // and keep track of what that identitytoken actually is.

    Binder.clearCallingIdentity();

    final long ident =Binder.clearCallingIdentity();

    for (;;) {

    Message msg = queue.next();// might block取消息队列中的一个待处理消息..

    if (msg == null) {

    // No message indicates thatthe message queue is quitting.

    return;

    }

    // This must be in a localvariable, 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 courseof dispatching the

    // identity of the thread wasn'tcorrupted.

    final long newIdent =Binder.clearCallingIdentity();

    if (ident != newIdent) {

    Log.wtf(TAG, "Threadidentity 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取出一个Message,调用msg.target.dispatchMessage(msg),target就是发送message时跟message关联的handler,这样就调用到了熟悉的dispatchMessage,Message被处理后会被recycle。当queue.next返回null时会退出消息循环,接下来就看一下MessageQueue.next是怎么取出消息的,又会在什么时候返回null。

    final Message next()

    {

    int pendingIdleHandlerCount = -1; // -1only during first iteration

    int nextPollTimeoutMillis = 0;

    for (;;) {

    if (nextPollTimeoutMillis != 0) {

    Binder.flushPendingCommands();

    }

    nativePollOnce(mPtr,nextPollTimeoutMillis);

    synchronized (this) {

    if (mQuiting) {

    return null;

    }

    // Try to retrieve the nextmessage.Return if found.

    final long now =SystemClock.uptimeMillis();

    Message prevMsg = null;

    Message msg = mMessages;

    if (msg != null &&msg.target == null) {

    // Stalled by abarrier.Find the next asynchronousmessage in the queue.

    do {

    prevMsg = msg;

    msg = msg.next;

    } while (msg != null&& !msg.isAsynchronous());

    }

    if (msg != null) {

    if (now < msg.when) {

    // Next message is notready.Set a timeout to wake up when itis ready.

    nextPollTimeoutMillis =(int) Math.min(msg.when - now, Integer.MAX_VALUE);

    } else {

    // Got a message.

    mBlocked = false;

    if (prevMsg != null) {

    prevMsg.next =msg.next;

    } else {

    mMessages =msg.next;

    }

    msg.next = null;

    if (false)Log.v("MessageQueue", "Returning message: " + msg);

    msg.markInUse();

    return msg;

    }

    } else {

    // No more messages.

    nextPollTimeoutMillis = -1;

    }

    // If first time idle, then getthe number of idlers to run.

    // Idle handles only run if thequeue is empty or if the first message

    // in the queue (possibly abarrier) is due to be handled in the future.

    if (pendingIdleHandlerCount< 0

    && (mMessages== null || now < mMessages.when)) {

    pendingIdleHandlerCount =mIdleHandlers.size();

    }

    if (pendingIdleHandlerCount<= 0) {

    // No idle handlers torun.Loop and wait some more.

    mBlocked = true;

    continue;

    }

    if (mPendingIdleHandlers ==null) {

    mPendingIdleHandlers = newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];

    }

    mPendingIdleHandlers =mIdleHandlers.toArray(mPendingIdleHandlers);

    }

    // Run the idle handlers.

    // We only ever reach this codeblock during the first iteration.

    for (int i = 0; i

    final IdleHandler idler =mPendingIdleHandlers[i];

    mPendingIdleHandlers[i] = null;// release the reference to the handler

    boolean keep = false;

    try {

    keep = idler.queueIdle();

    } catch (Throwable t) {

    Log.wtf("MessageQueue", "IdleHandler threwexception", t);

    }

    if (!keep) {

    synchronized (this) {

    mIdleHandlers.remove(idler);

    }

    }

    }

    // Reset the idle handler count to0 so we do not run them again.

    pendingIdleHandlerCount = 0;

    // While calling an idle handler, anew message could have been delivered

    // so go back and look again for apending message without waiting.

    nextPollTimeoutMillis = 0;

    }

    }

    MessageQueue.next首先会调用nativePollOnce,然后如果mQuiting为true就返回null,Looper就会退出消息循环。

    相关文章

      网友评论

          本文标题:Android消息处理机制(Handler、Looper、Mes

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