Android消息机制Looper与VSync的传播

#1 主要内容

本文主要简单记录在native层Looper消息机制的相关内容,主要着手于下面几个问题:
(1)消息机制的原理;
(2)VSync是如何借助消息机制进行传播的;

2 Android消息机制

2.1 应用进程的创建

说起Android的消息机制,先大致的理一下Android应用的运行机制,Android上层是基于消息机制的,首先考虑一个问题,描述如下:Android应用作为运行在Linux平台上的用户进程,是如何保持一致处于运行状态的?也就是如何做到主线程不结束,进程一直存在的?
要想搞清楚这个问题,首先得搞清楚Android应用进程是如何创建的,比如我们点击桌面上的icon,应用进程启动了,然后界面出现了,只要不退出,界面一直存在,这整个的过程是怎么样的呢?下面分几个步骤来说明这个问题:
(1)桌面Launcher应用也是一个Android应用,点击图标启动进程;
(2)ActivityManagerService接收到Activity的启动请求,发现需要启动的Activity所在的进程当前尚未运行,首先需要启动进程;
(3)进程启动后,Activity随之被启动,进入主线程的消息循环,保持“不死状态”;
本文主要讨论消息机制,为保持主题清晰,只对上述问题简单描述:
这里写图片描述
(网上某位大神画的图,因为是之前下载的,现在找不到作者了,引用一下,说明问题,切莫见怪)

上面的图画的很清楚:
(1)桌面Launcher应用收到点击事件,通过Binder IPC通信调用AMS中的startActivity;
(2)AMS首先调用Process.start()方法,从Zygote进程Fork出一个进程,并将ActivityThread.main函数作为主线程的入口,开始执行;
(3)应用进程启动后,会在ActivityThread.main函数中调用attach函数,该函数会远程调用AMS.attachApplication函数,向AMS注册当前的进程,然后AMS会远程调用应用的ActivityThread.bindApplication函数,创建一些关键的数据结构,后面AMS接收到Idle消息后,经由Binder向应用进程发送LAUNCH_ACTIVITY远程调用,从而开始启动Activity。
上面的最下面的部分,就是应用一直保持运行状态的原因,应用进程主线程里面创建了一个Looper循环,一直在等待消息,没有消息的时候线程会被阻塞住,从而保证主线程一直处于活跃状态。
这里写图片描述
这个图是Android应用启动后,整个的进程结构,里面包含了上面的图中没有包含的有关Android系统进程的相关内容,有兴趣的可以再研究一下。

2.2 Android消息机制

网上有很多资料,对于Android消息机制已经讲的很清楚,这里就不详细的介绍了,推荐老罗的 http://blog.csdn.net/luoshengyang/article/details/6817933 ,这里简单介绍一下Handler、Looper以及MessageQueue之间的关系,看一下图:
这里写图片描述
分两种情况来说明整个过程:
情形(1):应用线程中的MessageQueue中没有消息,线程处于阻塞状态;
情形(2):应用线程中MessageQueue中有消息,线程处于运行状态;

2.2.1 线程阻塞

线程阻塞的时候,会停止在MessageQueue.next函数中的nativePollOnce里面,超时时间设置成了-1,假设当前线程代号线程A,这时候只能由别的线程(比如线程B)向线程A的MessageQueue中enqueueMessage,看一下这个函数:

boolean enqueueMessage(Message msg, longwhen) {
            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 {
                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);    //唤醒处于阻塞状态的mPtr所对应的线程
            }
        }
        return true;
    }

如果当前线程处于阻塞状态,会调用到nativeWake函数,该函数会向native层的MessageQueue、Looper对应的pipe管道中写入一个W字符,epoll机制在监听pipe管道读事件的时候收到数据会从上述阻塞的nativePollOnce函数里面返回,返回到java层以后发现MessageQueue里面有未处理的消息,就可以接着处理了。

2.2.2 线程正在运行

一开始以为,如果线程处于运行状态,应该一直处理MessageQueue中的消息,直到全部都处理完,没有消息以后进入阻塞状态,整个的流程应该不会和native层有任何的关系,但是实际上代码不是这样实现的,线程在处理每一个消息的时候,都会执行native函数nativePollOnce,如果当前MessageQueue里面有消息,会将epoll的超时时间设置为0,epoll会立马返回,这时候就可以从nativePollOnce里面返回处理下一个消息,但是像上面的MessageQueue里面没有消息后nativePollOnce在执行的时候,超时时间标记被设置成了-1,在进入epoll之前会用一个很大的时间值来替代-1,从而让epoll机制无法返回,达到阻塞线程的目的。
说明:这个部分,老罗的博客上将的很清楚,实在不想也没有必要去细写了。

3 应用请求VSync同步信号

应用在请求重绘的时候会请求同步信号,而且是重绘一次,请求一次,不重绘的时候是不请求的,也就是正常情况下,若应用的界面不请求重绘,应用是接收不到VSync信号的。应用要想请求同步信号,可以借助Choreographer对象来实现,Choreographer的英文含义是“编舞”,是用来控制节奏的,名字起得确实挺生动的。
看一下Choreographer的定义,该类包含一个FrameDisplayEventReceiver对象,看一下代码:

 //Choreographer.java
   private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
   
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }

        @Override
        publicvoid onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            .............
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Messagemsg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        publicvoid run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

FrameDisplayEventReceiver这个类比较简单,主要的工作都在基类DisplayEventReceiver里面完成了,DisplayEventReceiver类里面有两个比较重要的函数,一个是构造器,还有一个就是scheduleVsync函数:

//DisplayEventReceiver.java
public abstract class DisplayEventReceiver {
   
    private final CloseGuard mCloseGuard = CloseGuard.get(); 
    private long mReceiverPtr; 
    // We keep a reference message queue object here so that it is not
    // GC'd while the native peer of the receiver is using them.
    private MessageQueue mMessageQueue; 
    /**
     * Creates a display event receiver.
     * @param looper The looper to use when invoking callbacks.
     */
    public DisplayEventReceiver(Looper looper) {
        if (looper == null) {
            thrownew IllegalArgumentException("looper must not be null");
        } 
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(this, mMessageQueue); 
        mCloseGuard.open("dispose");
    }
    /**
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.
     */
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            nativeScheduleVsync(mReceiverPtr);
        }
    }
}

该类是一个abstract类,构造器里面调用了nativeInit函数,当应用需要同步信号的时候,最终会调用到scheduleVsync函数,里面调用到了nativeScheduleVsync函数,这两个函数步骤比较多,下面分别来分析。

3.1 nativeInit

函数定义在android_view_DisplayEventReceiver.cpp里面:

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, receiverObj, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    } 
    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
    returnreinterpret_cast<jlong>(receiver.get());
}

(1)获取与当前应用主线程相关联的native层的MessageQueue对象;
(2)创建一个NativeDisplayEventReceiver对象;
(3)调用NativeDisplayEventReceiver.initialize函数;
下面逐个分析上面的2、3两个步骤。

3.1.1 NativeDisplayEventReceiver

该类定义在android_view_DisplayEventReceiver.cpp文件中,看一下定义:

class NativeDisplayEventReceiver : public LooperCallback {
public:
    NativeDisplayEventReceiver(JNIEnv* env, jobject receiverObj, const sp<MessageQueue>& messageQueue);
    status_t initialize();
    void dispose();
    status_t scheduleVsync();
protected:
    virtual ~NativeDisplayEventReceiver(); 
private:
    jobject mReceiverObjGlobal;
    sp<MessageQueue> mMessageQueue;
    DisplayEventReceiver mReceiver;
    bool mWaitingForVsync;

    virtual
  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值