Android消息机制深入了解

消息驱动概念

消息驱动:消息驱动是一种进程或线程的运行模式。内部、外部的各种事件都可以放到消息队列中按序处理。适用于大量的交互事件。
操作系统普遍都拥有消息驱动模式。虽然消息驱动的理念都是:将各种外部行为的事件转换为内部的消息,然后进行处理,但是不同的操作系统处理方式还是有点不一样的。

消息机制模型在Windows操作系统与Android操作系统

Windows进程:
Windows是将所有的消息加入到系统消息队列中,处理消息也仅仅由主线程进行处理。即一个总的消息队   列,一个主线程来处理消息。
Android进程:
Android每一个线程都有一个自己的消息队列,处理也可以在线程内进行处理。

图示:
Windows
这里写图片描述
Android
这里写图片描述

优缺点:
Windows:Windows的全局消息队列很容易成为程序的瓶颈,如果某个消息处理不能及时完成,整个进程都会挂起,而    且因为是全局队列,所以线程间频繁地同步也会带来更大系统开销。
Android:Android将消息队列存在于每个线程中,线程内部的消息发送完全没有额外的开销。线程内部的消息在本线程的消息队列中循环,除非必要才向另外的线程发送消息,因此,最大程度地减少了因线程同步带来的系统开销。Android的消息处理方式非常灵活,消息处理的代码可以集中在一起,也可以分散在各个Handler对象中,甚至每条Message都能有自己的消息处理方法。

Android与消息机制相关的类主要是:Looper、Handler、Message、MessageQueue

我们平时用的Looper、Message、Handler都是用Java实现的。但是Native层也有与之对应的Looper等。

Looper:线程的消息循环处理器。
注意:每个线程只能拥有一个Looper对象,Looper对象是用来管理MessageQueue.
线程创建的同时不会马上创建Looper对象,需要通过程序自己创建。但是Android UI线程在启动时会创建一个Looper对象。
Looper定义如下:

public final class Looper
{
    static final ThreadLocal<Looper>  sThreadLocal=new ThreadLocal<Looper>();
    private static Looper sMainLooper;
    final MessageQueue mQueue;
    final Thread mThread;

    private static void prepare(boolean quitAllowed){...}
    public static void prepareMainLooper(){...}
    public static Looper getMainLooper(){...}
    public static void loop(){...}
}

Looper中method解析:

prepare():创建Looper对象并保存在sThreadLocal中。(只能创建一次,创建多次报异常)
具体实现:

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

myLooper():用来获取Looper对象。
具体实现:

public static Looper myLooper(){
                 Return sThreadLocal.get();
}

getMainLooper():返回主线程的Looper对象。Android不能在应用直接获取主线程的Looper对象,而是放在了Looper类中。
具体实现:

 Public static Looper getMainLooper()
{
  Synchronized(Looper.class){
            return sMainLooper;
}
}

Loop():主要是用来根据target(Handler)分发消息队列(Looper)中的消息。一般在线程的run()方法中调用。
具体实现:

public static void loop(){
    final Looper me =myLooper();
    ........
    final MessageQueue queue =me.mQueue;
    .......
    for(,,,){
        Message msg=queue.next();
        if(msg=null){
            return;
        }
        msg.target.dispatchMessage(msg);
        ......
        msg.recycleUnchecked();
    }
}

创建一个线程Looper的demo:

 public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
//首先调用prepare()初始化MessageQueue与Looper,然后进入Loop循环

Handler

:消息发送接受和处理者。
:程序通过send方法或者post方法消息发送到消息队列中。
:对循环队列中属于自己处理的消息(Message)进行处理,通过handleMessage()方法。
***handler是将handleMessage方法中的代码引用压入主线程的执行堆栈。

Handler消息发送send方式与post方式详解:

Send方式:
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyDelayed(int what,long delayMills)
public final boolean sendEmptyMessageTime(int what,long uptimeMillis)
public final boolean sendMessageDelayed(Message msg,long delayMillis)
public boolean sendMessageTime(Message msg,long uptimeMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)

总结:
如果希望马上处理,但不打算插队,使用sendMessage()
如果非常紧急,希望尽快处理,使用sendMessageAtFrontOfQueue()
如果希望延时一段时间处理,使用sendMessageDelayed();
如果希望在指定时间处理,使用sendMessageAtTime();

Post方式

public final boolean post(Runable r){
return sendMessageDelayed(getPostMessage(r),0);
}
public final boolean postAtTime(Runnable r,long uptimeMillis){
return sendMessageAtTime(getPostMessage(r),uptimeMillis);
}
public final boolean postAtTime(Runable r,Object token,long uptimeMillis){
return sendMessageAtTime(getPostMessage(r,token),uptimeMillis);
}
public final boolean postDelayed(Runable r,long delayMillis){
return sendMessageDelayed(getPostMessage(r),delayMillis);
}
public final boolean postAtFrontOfQueue(Runable r){
return sendMessageAtFrontOfQueue(getPostMessage(r));
}

总结:
从post实现可以看出,post传的是带有处理方法的消息,将处理方法执行后依旧是用的send方式在发送消息。而send方法是发送传统的带有消息ID的消息。(post方法执行的是Runable run方法)而sendMessage执行的是handleMessage方法。

Handler外部发送消息是通过send和post,Handler内部依然是发送信息通过dispatchMessage()。
dispatchMessage()具体实现:

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

    }
}
//dispatchMessage优先分配消息中自带回调方法的消息。如果Handler定义了回调方法,先会调用这个方法处理,如果这个方法没有处理,还会调用Handler本身的handleMessage()方法

总结一下

:一个线程可以有多个Handler,但是一个线程只能有一个Looper,多个Handler对应一个Looper。就是说线程中消息队列只有一个,但是可以有多个接受和处理器用来处理不同的消息堆。

Message类

Message是消息的载体,Message设计成为Parcelable类的派生类,这表明Message可以通过binder  来跨进程发送。(Binder是Android进程通信的一种方式)
Message对象可以传递两类数据:整型和任意的对象,其中整数可以通过Message.arg1,Message.arg2和Message.what字段设置。
其中what是最常用的。

MessageQueue

1.MessageQueue的构造:
MessageQueue对象的构造方法是调用本地方法nativeInit()。nativeInit()方法对应的JNI函数是native的android_os_MessageQueue_nativeInit()
android_os_MessageQueue_nativeInit()具体实现如下:

static jint android_os_MessageQueue_nativeInit(JNIEnv* env,jclass clazz){
        NativeMessageQueue* nativeMessageQueue=new NativeMessageQueue();
        ....
    }

NativeMessageQueue构造函数如下:

 NativeMessageQueue:: NativeMessageQueue():mInCallback(false) ,mExceptionObj(NULL){
            mLooper=Looper::getForThread();
            if(mLooper==NULL){
                mLooper =new Looper(false){
                Looper::setForThread(mLooper);
                }
            }
        }

NativeMessageQueue是一个代理类,作用是把Java层的调用转变为native层Looper类的函数调用。
native层的Looper也实现了消息处理机制,但是只是用的它的等待唤醒机制,其余消息队列的实现还是在Java层。
那么我们来看下Native层的Looper类的构造函数代码:

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);

    // Allocate the epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
}
//从上面实现可以知道Looper干了两件事:⒈创建了管道,二是使用epoll来监听管道。Epoll的作用是监听管道上的数据,管道则用于线程间的通信。

2.MessageQueue中的消息处理过程
:next()是MessageQueue中的消息循环方法。
具体实现如下:

final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only 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 next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is 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 get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) 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 to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; 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 threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

next方法功能解析如下:
1.检查队列中的第一条消息是否为SyncBarrier消息,如果是,寻找队列中的第一条”异步消息”,找到后设为当前处理的消息;如果不是”SyncBarrier”,把第一条消息设为当前处理的消息
2.如果当前消息不为NULL,检查该消息的处理时间是否已经超时,如果没有,计算等待时间。如果处理的时间到了,next()方法将返回消息退出。
3.如果当前消息为NULL,表示队列中没有可以处理的消息,设置等待时间为-1.
4.检查队列中的退出标志,如果检测到退出标志则销毁native层的对象,然后退出next方法。
5.检查是否已经安装了idle状态的回调函数,如果没有安装,回到循环的开始,调用nativePollOnce()方法挂起线程并等待消息的到来。
6.如果安装了idle状态的回调函数则调用所有回调涵涵素,同时把epoll的等待时间设为0,这表明在idle处理函数的情况下消息队列的循环处理是不会阻塞的,这样idle处理函数将会不停的调用,直到处理函数值为false。

MessageQueue通过enqueueMessage方法发送消息。
enqueueMessage具体实现如下:

final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                        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 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;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

enqueueMessage方法会尽量地避免唤醒处理线程,插入一条马上处理的消息或者在暂停处理消息的情况下,又插入了“异步消息”的情况下才会唤醒处理线程。
而其余情况都是把消息放到队列的中部或尾部,如果前面还有消息处理,这条消息就更不着急去处理。

我们再来看一下唤醒函数wake() (唤醒线程最终还是要调用wake()函数)

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();//如果头部是Barrier并且新消息是异步消息则“有可能”需要唤醒    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;
}

综上我们将Looper,Handler,Message,MessageQueue都一一说明。

最后我要提一下Message类中有一个setAsynchronous()方法和MessageQueue中enqueueSyncBarrie()方法。
enqueueSyncBarrier()方法是发送一个不带Handler的消息到消息队列中,这样就导致此消息之后的消息暂停处理。只有那些使用setAsynchronous为消息做了标记可以继续处理。
之前我们看MessageQueue中next()的过程中会判断消息是否为SyncBarrier,如果是就阻塞消息队列,而setAsynchronous是为了能够让消息在这种情况下运行。

转载请标注好作者Mike_cw

最想要去的地方怎么能在半路返航

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值