《深入解析Android 5.0系统》——第6章,第6.4节Android的消息机制

6.4 Android的消息机制
深入解析Android 5.0系统
消息驱动是一种进程或线程的运行模式。内部、外部的各种事件都可以放到消息队列中按序处理。这种模式特别适合处理大量的交互事件。Android应用的UI线程,同样采用了消息驱动模式,所有外部来的按键消息、触屏消息、各种系统Intent、广播等都会转化为内部的消息,然后在主线程中分发处理。

6.4.1 消息模型
现在的操作系统普遍采用消息驱动模式。Windows操作系统就是典型的消息驱动类型。但是,Android的消息处理机制和Windows的消息处理机制不太相同。下面用图6.1来分别表示Windows和Android的消息机制模型。

在Windows的消息处理模型中,存在一个系统消息队列,这个队列是整个进程的核心,几乎所有动作都要转换成消息,然后放到这个队列中,消息的处理只能在主线程来完成。

Android的消息处理则不一样。Android没有全局的消息队列,消息队列是和某个线程关联在一起的。每个线程最多有一个消息队列,消息的取出和处理在线程中完成。

比较而言,Windows的消息模型较简单,消息的发送也简单方便。Android的消息模型相对复杂很多,使用前必须为线程构造消息队列,发送消息也必须先得到消息队列的Handler对象。但是Windows的全局消息队列很容易成为程序的瓶颈,如果某个消息处理不能及时完成,整个进程都会挂起,而且因为是全局队列,所以线程间频繁地同步也会带来更大系统开销。Android的消息机制则避免了这种情况,消息队列在各个线程中,线程内部的消息发送完全没有额外的开销。在程序设计时,可以根据需要在合适的线程中设置消息队列,线程内部的消息在本线程的消息队列中循环,除非必要才向另外的线程发送消息,因此,最大程度地减少了因线程同步带来的系统开销。Android的消息处理方式非常灵活,消息处理的代码可以集中在一起,也可以分散在各个Handler对象中,甚至每条Message都能有自己的消息处理方法。

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

1.Looper类

Looper对象是线程的消息循环处理器,每个线程只能有一个Looper对象。Looper内部有一个消息队列MessageQueue,所有线程的消息都存放在这个队列中。新创建一个线程时,系统并不会马上为这个线程创建一个Looper对象,需要程序自己创建。Android在启动时,为主线程(UI线程)创建了一个Looper对象。

2.Handler类

Handler对象是Message的接收者和处理者。用户使用Handler对象把Message添加到消息队列中;同时通过Handler的回调方法handleMessage()来对消息队列中的Message进行处理。Handler对象在构造时和某个Looper对象关联在一起。Handler和Looper是多对一的关系,多个Handler对象可以和一个Looper对象关联起来,反之则不行。

3.Message类

Message是消息的载体。Message设计成为Parcelable类的派生类,这表明Message可以通过binder来跨进程发送。

6.4.2 理解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类的实例对象,Looper类的实例必须通过方法prepare()创建。prepare()方法会创建一个Looper的对象,并把它保存在静态变量sThreadLocal中。一个线程中多次调用prepare()方法将会抛出异常。

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));
}
创建完Looper对象后,通过方法myLooper()可以获得Looper对象。

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

静态变量sThreadLocal的类型是模板类ThreadLocal,它通过将需要保存的对象和线程id关联在一起的方式实现了线程本地存储的功能,这样放入sThreadLocal对象中的Looper对象就和创建它的线程关联在一起了。

Looper类的getMainLooper()方法将返回主线程的Looper对象。Android应用启动时会创建主线程,同时会创建一个Looper对象和主线程相关联。但是创建主线程的代码在Framework中,应用层不能直接取得主线程的Looper对象。因此,Android将获得主线程Looper对象的方法放在了Looper类中,如下所示:

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

有了Looper类的对象后,可以调用Looper类的loop()方法来进入消息循环。loop()是一个静态的方法,它里面有一个无限for循环,对loop()方法的调用一般在线程的run()方法中。Looper类的典型用法如下:

class myThread extends Thread
{
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

loop()方法的主要作用是分发消息队列中的消息,函数的代码如下:

public static void loop() {
    final Looper me = myLooper();
    ......
    final MessageQueue queue = me.mQueue;
    ......
    for (;;) {
        Message msg = queue.next();    // 取一条消息,没有消息会阻塞
        if (msg == null) {
            return;        // msg等于null表示接到了退出的请求
        }
        ......
        msg.target.dispatchMessage(msg);   // 分发消息
        ......
        msg.recycleUnchecked ();
    }
}

loop()方法会循环从MessagQueue队列中取出消息,然后把消息分发出去。消息分发是通过Message对象中的target变量完成了,这个变量的类型是Handler,前面已经介绍了,一个Looper对象可以对应多个Handler对象,线程的Looper对象并不是只和一个Handler对象相关联。

Message是消息的载体,发送者把需要传递的消息放在Message对象中,Message对象创建的时候就需要指定它的处理对象。Handler主要用来处理消息,一个Handler对象可以处理多种消息。

6.4.3 理解Handler类
Handler主要负责消息的发送和处理。在一个线程中可以只用一个Handler对象来处理所有消息,也可以使用多个。构造一个Handler对象需要两个参数,线程的Looper对象和消息的处理函数。对于Handler对象而言,参数Looper是必须的,因为它只能给某个线程的Looper对象发送消息,如果构造方法不指定特定的Looper对象,它会使用当前线程的Looper对象。但是参数callback并不是必须的,应用程序可以通过这个callback方法来实现对消息的集中处理。也可以把处理消息的callback方法直接放在消息对象中。

Handler类是消息框架的一部分,消息的定义和响应还需要在应用层的代码中完成,在这一点上Android设计的非常灵活。传统的消息模型中,某个线程能处理的消息的种类必须预先定义好,使用者只能使用它们来给某个线程发送消息。但是Android把消息的定义和处理完全独立出来了,线程只是提供了一个消息队列和消息响应代码的运行环境。例如,Android主线程的实现都是在Framework中,但是我们可以使用下面的方法来构造一个带有callback方法的消息发送给主线程。

public static Message obtain(Handler h, Runnable callback)

这样,这个callback方法将在主线程中执行。

这也是面向过程和面向对象设计的区别。从面向过程的设计思路出发,我们需要给每种Message定义不同的消息Id,然后在一个函数里用switch语句来处理它们。而在面向对象的概念中,消息对象本身就是独一无二的,因此,不需要使用消息Id来相互区分,定义一种消息时只需要关心最核心的东西:消息的响应代码。同样,线程也独立出来了,负责消息处理的线程此时更像一个平台,各种消息都可以使用它,而不再局限于处理一些预定义的消息。

如果使用消息的目的只是希望处理函数能在一个不同的线程中执行,或者延时一段时间执行,而并不关心它在哪个线程中执行,我们可以让一个公共线程来承担执行的任务,而不用再为每种消息来定义一个线程类。

Android的消息框架中对上面介绍的两种方式都支持,只要明白了设计思路,看懂这些接口就很容易了。

Handler类的另外一个功能是发送消息。Handler类用来发送消息的接口也是很丰富的。从总体来讲,Handler类的消息发送接口分成两大类:一类是“send”类,另一类是“post”类。

1.“send”类的接口如下

public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)

所谓的“(send)发送”消息只是把消息插入到了消息队列中,同时指定消息处理的时间。如果指定的时间为0,表示要立即处理,MessageQueue会把这条消息插到队列的头部。MessageQueue类中接收消息的接口如下:

boolean enqueueMessage(Message msg, long when)
enqueueMessage()除了消息参数外,只有一个时间参数,因此,Handler类里面发送消息的接口虽然多,但是都是在时间上玩花样,让应用方便使用而已。“send”类方法总结如下:

如果希望马上处理,但是不打算插队,使用sendMessage();

如果非常紧急,希望尽快处理,使用sendMessageAtFrontOfQueue();

如果希望延时一段时间处理,使用sendMessageDelayed();

如果希望在指定时间处理,使用sendMessageAtTime()。

如果定义的消息只有消息Id,不用附加参数,使用sendEmptyMessage()方法将会更加方便。

2.“Post”类型的方法定义如下:

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

从代码的实现上看,这些“Post”方法也是在使用“send”类的方法在发送消息,只是它们的参数要求是Runnable类的对象,然后在方法中调用getPostMessage()获取了一个Message对象来发送。

看到这里就很好理解了:“post”类型的方法用来发送带有处理方法的消息,“send”类型的方法则用于发送传统的带有消息Id的消息。

最后,看看Handler类的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类的继承类,也可以通过重载这个HandleMessage()方法来达到处理消息的目的。

6.4.4 消息的同步——Message类的setAsynchronous()方法
在Android的Message类中,有一个setAsynchronous(boolean async)方法,从字面上理解,它给一条消息加上异步标志。难道消息不都是异步完成的吗?我们使用这个方法会把消息的执行模式变成同步的吗,还是不调用它,消息就是以同步的方式在执行?

在回答上面的问题前,我们先谈论编程中经常需要处理的一种情况:收到一条广播,或者在Activity刚启动时,可能需要完成一个比较耗时的操作,但是,如果消息处理函数长时间不返回,很容易发生ANR。常用的解决办法是发送一个消息,然后在消息处理函数中完成这个耗时操作。如果说发送消息有同步和异步之分,那么我们使用这种方法来解决ANR的问题是否在任何情况下都有效?

要理解setAsynchronous()方法的作用,需要仔细研究MessageQueue类的代码,在此之前,我们先说说这个问题的答案。如果我们希望用消息来避免ANR,使用普通消息就可以解决问题。因为从代码上看,所有消息都会先放到队列中,然后再在本线程的处理函数中处理。这样的机制能解决我们的问题,让接收广播或Activity的函数很快返回,避免ANR。

那么setAsynchronous()方法的作用又是什么呢? 在MessageQueue类中有一个方法enqueueSyncBarrier(long when),调用这个方法会在消息队列中插入一条没有Handler对象的消息,这条不带Handler对象的消息称为“SyncBarrier”,MessageQueue将暂停处理队列中“SyncBarrier”以后的消息。这好比一群人在排队买票,有人过来在队列中放了一块牌子:“从这开始,暂停销售”。但是这时如果还有消息需要处理,可以使用setAsynchronous()方法来给一条消息做上标志,MessageQueue检测到消息中的标志后,会正常处理这条消息,但是别的消息还是暂停处理,直到调用removeSyncBarrier()方法移走了挡在消息队列前面的“SyncBarrier”。

6.4.5 分析MessageQueue类
研究MessageQueue时,如果带着问题去分析,可以理解地更加透彻。先考虑下面的问题。

不同线程间发送消息,有同步保护吗?如何实现的?

消息队列不同于普通队列,每条消息都有时间,如何实现按时间分发消息?

没有消息时消息队列会挂起吗?来了新的消息又是如何唤醒的。

消息队列是如何组织的?新消息是如何插入的,都在队尾吗?

“SyncBarrier”是如何工作的?

下面我们将从对象构造、消息处理、发送消息3个方面来分析MessageQueue。

1.MessageQueue的构造

MessageQueue对象的构造是调用本地方法nativeInit()完成的。nativeInit()方法对应的JNI函数是native层的android_os_MessageQueue_nativeInit()函数,代码如下:

static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ......
}
android_os_MessageQueue_nativeInit()函数最主要的功能是新创建了一个本地的Native MessageQueue对象。NativeMessageQueue的构造函数如下:

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

NativeMessageQueue的构造函数只是创建一个本地的Looper类对象。从NativeMessageQueue类的代码看,它本质上是一个代理类。它把Java层的调用转变为对native层Looper类的函数调用,native层的Looper类才是关键所在。

native层的Looper类也实现了一套完整的消息处理机制。但是Java层的Looper类和native层的Looper类并没有直接关系。MessageQueue虽然使用了Native层的Looper类,但也只使用了它的等待/唤醒机制,其余的如消息队列的实现还是在Java层。因此,如果再看到MessageQueue有中从Java到native层之间的调用,可以略去中间过程,直接分析native层Looper类中的函数。

下面是本地Looper类的构造函数的代码:

Looper::Looper(bool allowNonCallbacks) :

    mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),

    mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {

int wakeFds[2];

int result = pipe(wakeFds);  //创建匿名管道   .

mWakeReadPipeFd = wakeFds[0];  

mWakeWritePipeFd = wakeFds[1];  

//把读写管道都设成非阻塞式

result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);

result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); 

mIdling = false;

mEpollFd = epoll_create(EPOLL_SIZE_HINT);  //创建和初始化epoll对象    

struct epoll_event eventItem;

memset(&amp; eventItem, 0, sizeof(epoll_event));

eventItem.events = EPOLLIN;

eventItem.data.fd = mWakeReadPipeFd;

//把“读管道”加入到epoll监听中

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &amp; eventItem);
}

在上面的代码中,Looper类的构造函数做了两件事情,一是创建了管道,二是使用epoll来监听读管道。epoll的作用是监听管道上的数据,管道则用于线程间通信。

2.MessageQueue中的消息处理过程

MessageQueue中的消息循环在方法next()中,为了能让读者更好地理解next()方法,笔者在代码中做了比较详细的注释,如下所示:

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
       return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 调用本地方法等待nextPollTimeoutMillis毫秒, -1表示要永远阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);
        // 这里使用了针对this对象同步,因此只要next方法还没退出
        // 再调用本对象的任何方法都将导致调用线程挂起
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;        // mMessages指向队列头
            if (msg != null && msg.target == null) {
                // “SyncBarrier”的标志就是其消息的target为null
                // 如果队列的第一条消息是“SyncBarrier”,忽略普通消息,查找第一条“异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {  // 找到了第一条消息
               if (now < msg.when) {
                    //如果还没有到处理这条消息的时间,计算需要等待的时长
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now,
                                                Integer.MAX_VALUE);
                } else {
                    mBlocked = false;  // 取消“阻塞”标志
                    // 将要处理的消息从队列中断开
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                //返回消息

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

                return msg;

            }

        } else {  // 表示队列中没有现在必须处理的消息了 

            nextPollTimeoutMillis = -1;

        }

        if (mQuitting) { // 如果退出标志设置了,则销毁native对象,然后返回

            dispose();

            return null;

        }                     

        // 第一次进入idle会检查是否安装了idle handler

        if (pendingIdleHandlerCount &lt; 0

                &amp;&amp; (mMessages == null || now &lt; mMessages.when)) {

            pendingIdleHandlerCount = mIdleHandlers.size();

        }

        if (pendingIdleHandlerCount &lt;= 0) {

            mBlocked = true;

            continue;  // 没有安装idle handler则继续for循环

        }

        // idle handler放入数组mPendingIdleHandlers中

        if (mPendingIdleHandlers == null) {

            mPendingIdleHandlers = new IdleHandler[Math.max(

                                      pendingIdleHandlerCount, 4)];

        }

        mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

    }

    // 处理所有idle handler,如果回调结果为false,表示不再继续处理

    // 则 从idle handler的列表中移除该handler

    for (int i = 0; i &lt; pendingIdleHandlerCount; i++) {

        final IdleHandler idler = mPendingIdleHandlers[i];

        mPendingIdleHandlers[i] = null; 

        boolean keep = false;

        try {

            keep = idler.queueIdle();

        } catch (Throwable t) {

            Log.wtf("MessageQueue", "IdleHandler threw exception", t);

        }

        if (!keep) {

            synchronized (this) {

                mIdleHandlers.remove(idler);

            }

        }

    }

    pendingIdleHandlerCount = 0;

    //如果有idle handler存在,把nextPollTimeoutMillis设为0,让循环继续,而不是阻塞

    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。

next()方法中会调用nativePollOnce()方法。nativePollOnce()方法最后通过调用epoll_wait()来执行等待操作,如下面代码所示:

epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
上面代码中的mEpoolFd对象已经在Looper的构造函数里加入了对“读管道”的监听。如果wait的时间到,或者向“写管道”中写数据,epoll_wait()就会返回,阻塞结束。

3.向MessageQueue发消息

向MessageQueue发消息使用的是enqueueMessage方法,代码如下:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {  // 如果消息的target为NULL,抛出异常
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {       // 如果加入的是正在处理的消息对象,抛出异常
        throw new IllegalArgumentException(msg + " This message is already in use.");
    }
    synchronized (this) {    // 使用this对象来同步
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;   // p指向消息队列头
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // 队列中没有消息,或者消息需要插到队列头
            msg.next = p;
            mMessages = msg;        // 把消息插到队列头
            needWake = mBlocked;   // 这时如果处理线程阻塞了,则需要唤醒
        } else {
            // 如果设置了“SyncBarrier”,只有插入了“异步消息”才需要唤醒
            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()方法插入消息时根据时间来排序,时间早的插在前面。

消息队列的组织也非常简单,利用了Message类中的next指针形成一个从头指向尾的单向链表。插入时计算是否需要唤醒处理线程。enqueueMessage()方法会尽量地避免唤醒处理线程,只有插入了一条马上要处理的消息,或者在暂停处理消息的情况下,又插入了“异步消息”的情况下才会去唤醒处理线程。

为什么其余情况下插入消息不需要唤醒处理线程呢?其余的情况都是把消息放到队列的中部或尾部(时间未到)。如果前面还有消息没处理,这条消息就更不着急去处理了。

最后,让我们看看nativeWake()如何唤醒处理线程。nativeWake()最终会调用到native层的Looper类的wake()方法,代码如下:

void Looper::wake() {

ssize_t nWrite;

do {

    nWrite = write(mWakeWritePipeFd, "W", 1);

} while (nWrite == -1 &amp;&amp; errno == EINTR);

......
}

从上面的代码可以看到,wake()方法通过向管道中写入数据来唤醒消息处理线程。处理线程通过epoll监听管道上的数据,一旦有数据到来,线程就会被唤醒,next()方法会继续处理消息。

分析到这里,应该已经能够回答本节前面提出的问题了。而且从代码中我们也了解到,通过安装idle handler可以得到消息队列进入idle的通知。因此,一旦消息队列为空,应用就可以运行一些优先级较低但是耗时的代码。这些代码将在不影响程序消息处理的情况下,利用空闲时间来运行。``

时间: 2024-11-03 22:05:36

《深入解析Android 5.0系统》——第6章,第6.4节Android的消息机制的相关文章

《深入解析Android 5.0系统》——第1章,第1.4节下载源码

1.4 下载源码 深入解析Android 5.0系统 对于国内的开发者而言,下载Android的源码从来不是一件简单的事.因为一些原因,目前国内已经不能访问Android的源码网站了,最近好像连Android的官方网站也访问不了.对公司而言这不是难题,因为很多公司都有国外的VPN账号或者海外服务器.笔者下载Android的源码就是通过亚马逊的云服务器完成的.只要有国内大型银行的信用卡帐号,就可以在亚马逊平台上免费开通一个EC2服务器(免费使用期一年).亚马逊提供的带宽差不多有一个G,不到半个小时

《深入解析Android 5.0系统》——导读

目 录 前言 第1章 建立Android系统开发环境 1.1 安装操作系统1.2 安装开发包 1.3 安装一些有用的工具 1.4 下载源码 第2章 Android的编译环境-- Build系统 第3章 连接Android和Linu内核的 桥梁--Android的Bionic 进程间通信--Android 的Binder 第5章 连接Java和C/C++层的 关键--Android的JNI 第6章 Android的同步和消息机制 第6章 Android的同步和消息机制 6.1 原子操作 6.2 A

《深入解析Android 5.0系统》——第6章,第6.2节Android native层的同步方法

6.2 Android native层的同步方法 深入解析Android 5.0系统 Android在Linux提供的线程同步函数的基础上进行了二次封装,让实现线程同步更加简单方便.这些同步类和函数在native层的代码中出现的非常频繁. 6.2.1 互斥体Mutex和自动锁Autolock Mutex和Autolock是Android native层最常见的一种临界区保护手段,Autolock只是提供了一种更简便的使用Mutex的方法. Mutex是一个C++的类,它的接口如下所示: clas

《深入解析Android 5.0系统》——第1章,第1.1节安装操作系统

第1章 建立Android系统开发环境 深入解析Android 5.0系统 在开始研究Android系统之前,需要预先准备好系统开发需要的各种资源:包括操作系统.各种开发工具以及Android源码等.本章将介绍这些资源的获取途径和安装方法. 在阅读本书前,读者需要掌握一些必要的技能.Android应用使用Java语言开发,底层使用C/C++开发,因此,掌握Java 语言和 C/C++语言是进行Android 系统开发的必要条件.Android运行在Linux内核(从标准的Linux内核修改移植而

《深入解析Android 5.0系统》——第6章,第6.3节Android Java层的同步机制

6.3 Android Java层的同步机制 深入解析Android 5.0系统 Java语言和C/C++语言不一样,Java语言中提供了同步关键字synchronized来支持线程间的同步操作. 6.3.1 同步关键字synchronized synchronized关键字最常见的用法是保护一段代码,如下所示: class Foo implements Runnable { private String mLock; public void lockedMethod() { ...... sy

《深入解析Android 5.0系统》——第1章,第1.2节安装开发包

1.2 安装开发包 深入解析Android 5.0系统 Android系统的编译需要依赖一些第三方的开发包和工具,包括Oracle的JDK(以前属于Sun公司).大部分的软件包都能通过apt-get来安装和升级,非常方便,但是JDK不能通过这种方式安装,只能从Oracle的官方网站下载软件包手动安装.从Android 5.0开始,Google支持使用OpendJDK 1.7来编译Android,因此,我们又可以使用apt-get快速地安装编译环境.对于Android 5.0以前的代码,还是需要使

《深入解析Android 5.0系统》——第6章,第6.5节进程间的消息传递

6.5 进程间的消息传递深入解析Android 5.0系统Android的消息可以在进程之间传递.进程间消息传递是建立在Binder通信基础之上的.Binder本身用来在进程间传递信息已经足够了,这里介绍的进程间消息传递方法只是让应用在设计上更加便利,并不是架构上大的改进. 我们知道,只要有了Binder的引用对象就可以调用其功能.Android中如果希望向另一个进程的Handler发送消息,一定要通过某个Binder对象来代理完成.在Handler类中,方法getIMessage()会创建一个

《深入解析Android 5.0系统》——第1章,第1.3节安装一些有用的工具

1.3 安装一些有用的工具深入解析Android 5.0系统在开发和学习Android的过程中,一些辅助工具会非常有用,下面介绍几种必备的工具.另外还有一些有用的小工具,在后面的章节中会穿插介绍. 1.3.1 安装Android SDK编译Android的源码并不需要Android SDK,但是Android SDK 中附带了很多有用的工具,如adb.ddms.hierarchyviewer等,都是进行Android系统开发调试必须用到的. Android SDK需要从Android的官方网站中

《深入解析Android 5.0系统》——第6章,第6.1节原子操作

6.1 原子操作深入解析Android 5.0系统对简单类型的全局变量进行操作时,即使是一些简单的操作,如加法.减法等,在汇编级别上也需要多条指令才能完成.整个操作的完成需要先读取内存中的值,在CPU中计算,然后再写回内存中.如果中间发生了线程切换并改变了内存中的值,这样最后执行的结果就会发生错误.避免这种问题发生的最好办法就是使用原子操作. 原子操作中没有使用锁,从效率上看要比使用锁来保护全局变量划算.但是,原子操作也不是没有一点性能上的代价,因此还是要尽量避免使用. Android中用汇编语