zoukankan      html  css  js  c++  java
  • 《深入理解Android 卷III》第二章 深入理解Java Binder和MessageQueue

    《深入理解Android 卷III》即将公布。作者是张大伟。此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分。在一个特别讲究颜值的时代。本书分析了Android 4.2中WindowManagerService、ViewRoot、Input系统、StatusBar、Wallpaper等重要“颜值绘制/处理”模块

    注意,此处连载的节选是出版社未排版的内容。

    第2章 深入理解Java Binder和MessageQueue

    本章主要内容:

    ·  介绍Binder系统的Java层框架

    ·  介绍MessageQueue

    本章所涉及的源码文件名称及位置:

    ·  IBinder.java

    frameworks/base/core/java/android/os/IBinder.java

    ·  Binder.java

    frameworks/base/core/java/android/os/Binder.java

    ·  BinderInternal.java

    frameworks/base/core/java/com/android/intenal/os/BinderInternal.java

    ·  android_util_Binder.cpp

    frameworks/base/core/jni/android_util_Binder.cpp

    ·  SystemServer.java

    frameworks/base/services/java/com/android/servers/SystemServer.java

    ·  ActivityManagerService.java

    frameworks/base/services/java/com/android/servers/ActivityManagerService.java

    ·  ServiceManager.java

    frameworks/base/core/java/android/os/ServiceManager.java

    ·  ServcieManagerNative.java

    frameworks/base/core/java/android/os/ServcieManagerNative.java

    ·  MessageQueue.java

    frameworks/base/core/java/android/os/MessageQueue.java

    ·  android_os_MessageQueue.cpp

    frameworks/base/core/jni/android_os_MessageQueue.cpp

    ·  Looper.cpp

    frameworks/base/native/android/Looper.cpp

    ·  Looper.h

    frameworks/base/include/utils/Looper.h

    ·  android_app_NativeActivity.cpp

    frameworks/base/core/jni/android_app_NativeActivity.cpp

    2.1 概述

    由于本书所介绍的内容主要是以Java层的系统服务为主,因此Binder相关的应用在本书中比比皆是。

    而MessageQueue作为Android中重要的任务调度工具,它的使用也是随处可见。所以本书有必要对这两个工具有所介绍。依据邓凡平的允许与推荐,本章由卷II第2章升级到4.2.2而来,并且添加了对AIDL相关的知识点的分析。

    以本章作为本书Android分析之旅的开篇,将重点关注两个基础知识点,它们是:

    ·  Binder系统在Java世界是怎样布局和工作的。

    ·  MessageQueue的新职责。

    先来分析Java层中的Binder。

    建议 读者先阅读《深入理解Android:卷I》(以下简称“卷I”)的第6章“深入理解Binder”。网上有样章可下载。

    2.2 Java层中的Binder分析

    2.2.1 Binder架构总览

    假设读者读过卷I第6章“深入理解Binder”。相信就不会对Binder架构中代表Client的Bp端及代表Server的Bn端感到陌生。Java层中Binder实际上也是一个C/S架构,并且其在类的命名上尽量保持与Native层一致。因此可觉得,Java层的Binder架构是Native层Binder架构的一个镜像。Java层的Binder架构中的成员如图2-1所看到的。


    图 2 - 1 Java层中的Binder家族

    由图2-1可知:

    ·   系统定义了一个IBinder接口类以及DeathRecepient接口。

    ·  Binder类和BinderProxy类分别实现了IBinder接口。当中Binder类作为服务端的Bn的代表,而BinderProxy作为client的Bp的代表。

    ·  系统中还定义一个BinderInternal类。

    该类是一个仅供Binder框架使用的类。它内部有一个GcWatcher类,该类专门用于处理和Binder相关的垃圾回收。

    ·  Java层相同提供一个用于承载通信数据的Parcel类。

    注意 IBinder接口类中定义了一个叫FLAG_ONEWAY的整型,该变量的意义非常重要。当client利用Binder机制发起一个跨进程的函数调用时,调用方(即client)通常会堵塞。直到服务端返回结果。这样的方式和普通的函数调用是一样的。

    可是在调用Binder函数时,在指明了FLAG_ONEWAY标志后。调用方仅仅要把请求发送到Binder驱动就可以返回,而不用等待服务端的结果,这就是一种所谓的非堵塞方式。在Native层中,涉及的Binder调用基本都是堵塞的。可是在Java层的framework中。使用FLAG_ONEWAY进行Binder调用的情况非常多,以后经常会碰到。

    思考 使用FLAG_ONEWAY进行函数调用的程序在设计上有什么特点?这里简单分析一下:对于使用FLAG_ONEWAY的函数来说,client仅向服务端发出了请求,可是并不能确定服务端是否处理了该请求。

    所以。client通常会向服务端注冊一个回调(相同是跨进程的Binder调用),一旦服务端处理了该请求,就会调用此回调来通知client处理结果。当然,这样的回调函数也大多採用FLAG_ONEWAY的方式。

    2.2.2 初始化Java层Binder框架

    尽管Java层Binder系统是Native层Binder系统的一个Mirror,但这个Mirror终归还需借助Native层Binder系统来开展工作,即Mirror和Native层Binder有着千丝万缕的关系。一定要在Java层Binder正式工作之前建立这样的关系。以下分析Java层Binder框架是怎样初始化的。

    在Android系统中。在Java初创时期。系统会提前注冊一些JNI函数。当中有一个函数专门负责搭建Java Binder和Native Binder交互关系。该函数是register_android_os_Binder,代码例如以下:

    [android_util_Binder.cpp-->register_android_os_Binder()]

    int register_android_os_Binder(JNIEnv* env)

    {

        // 初始化Java Binder类和Native层的关系

        if(int_register_android_os_Binder(env) < 0)

           return -1;

        // 初始化Java BinderInternal类和Native层的关系

        if(int_register_android_os_BinderInternal(env) < 0)

           return -1;

        // 初始化Java BinderProxy类和Native层的关系

        if(int_register_android_os_BinderProxy(env) < 0)

           return -1;

        ......

        return0;

    }

    据上面的代码可知,register_android_os_Binder函数完毕了Java Binder架构中最重要的3个类的初始化工作。

    1. Binder类的初始化

    int_register_android_os_Binder函数完毕了Binder类的初始化工作,代码例如以下:

    [android_util_Binder.cpp-->int_register_android_os_Binder()]

    static int int_register_android_os_Binder(JNIEnv*env)

    {

        jclassclazz;

       //kBinderPathName为Java层中Binder类的全路径名。“android/os/Binder“

        clazz =env->FindClass(kBinderPathName);

        /*gBinderOffSets是一个静态类对象。它专门保存Binder类的一些在JNI层中使用的信息,

          如成员函数execTranscat的methodID,Binder类中成员mObject的fildID */

       gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

       gBinderOffsets.mExecTransact

                         =env->GetMethodID(clazz, "execTransact", "(IIII)Z");

       gBinderOffsets.mObject

                         =env->GetFieldID(clazz, "mObject", "I");

        // 注冊Binder类中native函数的实现

        returnAndroidRuntime::registerNativeMethods(

                                env,kBinderPathName,

                                gBinderMethods,NELEM(gBinderMethods));

    }

    从上面代码可知。gBinderOffsets对象保存了和Binder类相关的某些在JNI层中使用的信息。

    它们将用来在JNI层对Java层的Binder对象进行操作。

    execTransact()函数以及mObject成员的用途将在2.2.3节介绍。

    建议 假设读者对JNI不是非常清楚。可參阅卷I第2章“深入理解JNI”。

    2. BinderInternal类的初始化

    下一个初始化的类是BinderInternal。其代码在int_register_android_os_BinderInternal函数中。

    [android_util_Binder.cpp-->int_register_android_os_BinderInternal()]

    static intint_register_android_os_BinderInternal(JNIEnv* env)

    {

       jclassclazz;

       // 依据BinderInternal的全路径名找到代表该类的jclass对象。

    全路径名为

       //“com/android/internal/os/BinderInternal”

       clazz =env->FindClass(kBinderInternalPathName);

      //gBinderInternalOffsets也是一个静态对象,用来保存BinderInternal类的一些信息

      gBinderInternalOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

       // 获取forceBinderGc的methodID

      gBinderInternalOffsets.mForceGc

                    = env->GetStaticMethodID(clazz, "forceBinderGc","()V");

       // 注冊BinderInternal类中native函数的实现

       returnAndroidRuntime::registerNativeMethods(

                             env,kBinderInternalPathName,

                            gBinderInternalMethods, NELEM(gBinderInternalMethods));

    }

    int_register_android_os_BinderInternal的工作内容和int_register_android_os_Binder的工作内容相似:

    ·  获取一些实用的methodID和fieldID。这表明JNI层一定会向上调用Java层的函数。

    ·  注冊相关类中native函数的实现。

    3. BinderProxy类的初始化

    int_register_android_os_BinderProxy完毕了BinderProxy类的初始化工作,代码稍显复杂。例如以下所看到的:

    [android_util_Binder.cpp-->int_register_android_os_BinderProxy()]

    static intint_register_android_os_BinderProxy(JNIEnv* env)

    {

        jclassclazz;

      

        // ① gWeakReferenceOffsets用来和WeakReference类打交道

        clazz =env->FindClass("java/lang/ref/WeakReference");

       gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

        // 获取WeakReference类get函数的MethodID

       gWeakReferenceOffsets.mGet= env->GetMethodID(clazz, "get",

                                       "()Ljava/lang/Object;");

        // ② gErrorOffsets用来和Error类打交道

        clazz =env->FindClass("java/lang/Error");

       gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

        // ③ gBinderProxyOffsets用来和BinderProxy类打交道

        clazz =env->FindClass(kBinderProxyPathName);

       gBinderProxyOffsets.mClass = (jclass) env->NewGlobalRef(clazz);

       gBinderProxyOffsets.mConstructor= env->GetMethodID(clazz,"<init>", "()V");

        ......//获取BinderProxy的一些信息

        // ④ gClassOffsets用来和Class类打交道

        clazz =env->FindClass("java/lang/Class");

       gClassOffsets.mGetName =env->GetMethodID(clazz,

                                  "getName","()Ljava/lang/String;");

        // 注冊BinderProxy native函数的实现

        returnAndroidRuntime::registerNativeMethods(env,

             kBinderProxyPathName,gBinderProxyMethods,

                                   NELEM(gBinderProxyMethods));

    }

    据上面代码可知,int_register_android_os_BinderProxy函数除了初始化BinderProxy类外,还获取了WeakReference类和Error类的一些信息。

    看来BinderProxy对象的生命周期会托付WeakReference来管理。难怪JNI层会获取该类get函数的MethodID。

    至此。Java Binder几个重要成员的初始化已完毕。同一时候在代码中定义了几个全局静态对象,各自是gBinderOffsets、gBinderInternalOffsets和gBinderProxyOffsets。

    框架的初始化事实上就是提前获取一些JNI层的使用信息,如类成员函数的MethodID。类成员变量的fieldID等。这项工作是必需的,由于它能节省每次使用时获取这些信息的时间。

    当Binder调用频繁时,这些时间累积起来还是不容小觑的。

    另外,这个过程中所创建的几个全局静态对象为JNI层訪问Java层的对象提供了依据。而在每个初始化函数中所运行的registerNativeMethods()方法则为Java层訪问JNI层打通了道路。

    换句话说。Binder初始化的工作就是通过JNI建立起Native Binder与Java Binder之间互相通信的桥梁。

    以下通过一个样例来分析Java Binder的工作流程。

    2.2.3 窥一斑,可见全豹乎

    这个样例源自ActivityManagerService,我们试图通过它揭示Java层Binder的工作原理。先来描写叙述一下该样例的分析步骤:

    ·  首先分析AMS怎样将自己注冊到ServiceManager。

    ·  然后分析AMS怎样响应client的Binder调用请求。

    本例的起点是setSystemProcess,其代码例如以下所看到的:

    [ActivityManagerService.java-->ActivityManagerService.setSystemProcess()]

    public static void setSystemProcess() {

       try {

          ActivityManagerService m = mSelf;

           // 将ActivityManagerService服务注冊到ServiceManager中

          ServiceManager.addService("activity", m);......

       } catch {... }

       return;

    }

    上面所看到的代码行的目的是将ActivityManagerService服务(以后简称AMS)加到ServiceManager中。

    在整个Android系统中有一个Native的ServiceManager(以后简称SM)进程。它统筹管理Android系统上的全部Service。成为一个Service的首要条件是先在SM中注冊。以下来看Java层的Service是怎样向SM注冊的。

    1. 向ServiceManager注冊服务

    (1)创建ServiceManagerProxy

    向SM注冊服务的函数叫addService。其代码例如以下:

    [ServiceManager.java-->ServiceManager.addService()]

    public static void addService(String name, IBinderservice) {

        try {

            //getIServiceManager返回什么

           getIServiceManager().addService(name, service);

        }

        ......

    }

    首先须要搞清楚getIServiceManager()方法返回的是一个什么对象呢?參考事实上现:

    [ServiceManager.java-->ServiceManager.getIServiceManager()]

    private static IServiceManagergetIServiceManager() {

        ......

        // 调用asInterface。传递的參数类型为IBinder       

       sServiceManager = ServiceManagerNative.asInterface(

                           BinderInternal.getContextObject());

        returnsServiceManager;

    }

    asInterface()方法的參数为BinderInternal.getContextObject()的返回值。于是这个简短的方法中有两个内容值得讨论:BinderInternal.getContextObject()以及asInterface()。

    BinderInternal.getContextObject()方法是一个native的函数,參考事实上现:

    [android_util_Binder.cpp-->android_os_BinderInternal_getContextObject()]

    static jobjectandroid_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)

    {

        /* 以下这句代码在卷I第6章具体分析过。它将返回一个BpProxy对象,当中

          NULL(即0,用于标识目的端)指定Proxy通信的目的端是ServiceManager*/

       sp<IBinder> b = ProcessState::self()->getContextObject(NULL);

        // 由Native对象创建一个Java对象,以下分析该函数

        returnjavaObjectForIBinder(env, b);

    }

    可见,Java层的ServiceManager须要在Native层获取指向Native进程中ServiceManager的BpProxy。这个BpProxy不能由Java层的ServiceManager直接使用。于是android_os_BinderInteral_getContextObject()函数通过javaObjectForIBinder()函数将创建一个封装了这个BpProxy的一个Java对象并返回给调用者。ServiceManager便可一通过这个Java对象实现对BpProxy的訪问。參考这个Java对象的创建过程:

    [android_util_Binder.cpp-->javaObjectForIBinder()]

    jobject javaObjectForIBinder(JNIEnv* env, constsp<IBinder>& val)

    {

        //mProxyLock是一个全局的静态CMutex对象

       AutoMutex _l(mProxyLock);

        /* val对象实际类型是BpBinder。读者可自行分析BpBinder.cpp中的findObject函数。

          事实上,在Native层的BpBinder中有一个ObjectManager,它用来管理在Native BpBinder

          上创建的Java BpBinder对象。以下这个findObject用来推断gBinderProxyOffsets

          是否已经保存在ObjectManager中。

    假设是。那就须要删除这个旧的object */

        jobjectobject = (jobject)val->findObject(&gBinderProxyOffsets);

        if(object != NULL) {

           jobject res = env->CallObjectMethod(object,gWeakReferenceOffsets.mGet);

           android_atomic_dec(&gNumProxyRefs);

           val->detachObject(&gBinderProxyOffsets);

           env->DeleteGlobalRef(object);

        }

       

        // ① 创建一个新的BinderProxy对象。

    并将它注冊到Native BpBinder对象的ObjectManager中

        object =env->NewObject(gBinderProxyOffsets.mClass,

                               gBinderProxyOffsets.mConstructor);

        if(object != NULL) {

            /* ② 把Native层的BpProxy的指针保存到BinderProxy对象的成员字段mObject中。

              于是BinderProxy对象的Native方法能够通过mObject获取BpProxy对象的指针。

              这个操作是将BinderProxy与BpProxy联系起来的纽带 */

           env->SetIntField(object, gBinderProxyOffsets.mObject,(int)val.get());

           val->incStrong(object);

           jobject refObject = env->NewGlobalRef(

                   env->GetObjectField(object, gBinderProxyOffsets.mSelf));

            /* 将这个新创建的BinderProxy对象注冊(attach)到BpBinder的ObjectManager中,

              同一时候注冊一个回收函数proxy_cleanup。

    当BinderProxy对象撤销(detach)的时候,

              该函数会被调用,以释放一些资源。读者可自行研究proxy_cleanup函数*/

           val->attachObject(&gBinderProxyOffsets, refObject,

                                 jnienv_to_javavm(env), proxy_cleanup);

            //DeathRecipientList保存了一个用于死亡通知的list

           sp<DeathRecipientList> drl = new DeathRecipientList;

           drl->incStrong((void*)javaObjectForIBinder);

            //将死亡通知list和BinderProxy对象联系起来

           env->SetIntField(object, gBinderProxyOffsets.mOrgue,

                                reinterpret_cast<jint>(drl.get()));

            // 添加该Proxy对象的引用计数

           android_atomic_inc(&gNumProxyRefs);

            /*以下这个函数用于垃圾回收。创建的Proxy对象一旦超过200个。该函数

              将调用BinderInter类的ForceGc做一次垃圾回收 */

           incRefsCreated(env);

        }

        returnobject;

    }

    BinderInternal.getContextObject的代码有点多,简单整理一下。可知该函数完毕了以下两个工作:

    ·  创建了一个Java层的BinderProxy对象。

    ·  通过JNI。该BinderProxy对象和一个Native的BpProxy对象挂钩,而该BpProxy对象的通信目标就是ServiceManager。

    接下来讨论asInterface()方法。大家还记得在Native层Binder中那个著名的interface_cast宏吗?在Java层中,尽管没有这样的宏。可是定义了一个相似的函数asInterface。

    以下来分析ServiceManagerNative类的asInterface函数,其代码例如以下:

    [ServiceManagerNative.java-->ServiceManagerNative.asInterface()]

    static public IServiceManager asInterface(IBinderobj)

    {

        ......// 以obj为參数。创建一个ServiceManagerProxy对象

        returnnew ServiceManagerProxy(obj);

    }

    上面代码和Native层interface_cast非常相似,都是以一个BpProxy对象为參数构造一个和业务相关的Proxy对象。比如这里的ServiceManagerProxy对象。ServiceManagerProxy对象的各个业务函数会将相应请求打包后交给BpProxy对象。终于由BpProxy对象发送给Binder驱动以完毕一次通信。

    说明 实际上BpProxy也不会直接和Binder驱动交互,真正和Binder驱动交互的是IPCThreadState。

    (2)addService函数分析

    如今来分析ServiceManagerProxy的addService函数,其代码例如以下:

    [ServcieManagerNative.java-->ServiceManagerProxy.addService()]

    public void addService(String name, IBinderservice)

                               throwsRemoteException {

        Parceldata = Parcel.obtain();

        Parcelreply = Parcel.obtain();

       data.writeInterfaceToken(IServiceManager.descriptor);

       data.writeString(name);

        // 注意以下这个writeStrongBinder函数,后面我们会具体分析它

       data.writeStrongBinder(service);

        /*mRemote实际上就是BinderProxy对象。调用它的transact。将封装好的请求数据

          发送出去 *

       mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);

        reply.recycle();

       data.recycle();

    }

    BinderProxy的transact,是一个native函数。事实上现函数的代码例如以下所看到的:

    [android_util_Binder.cpp-->android_os_BinderProxy_transact()]

    static jbooleanandroid_os_BinderProxy_transact(JNIEnv* env, jobject obj,

                                               jint code, jobject dataObj,

                                               jobject replyObj, jint flags)

    {

        ......

        // 从Java的Parcel对象中得到作为參数的Native的Parcel对象

        Parcel*data = parcelForJavaObject(env, dataObj);

        if (data== NULL) {

           return JNI_FALSE;

        }

        // 得到一个用于接收回复的Parcel对象

        Parcel*reply = parcelForJavaObject(env, replyObj);

        if(reply == NULL && replyObj != NULL) {

           return JNI_FALSE;

        }

        // 从Java的BinderProxy对象中得到之前已经创建好的那个Native的BpBinder对象

        IBinder*target = (IBinder*)

           env->GetIntField(obj, gBinderProxyOffsets.mObject);

        ......

        // 通过Native的BpBinder对象。将请求发送给ServiceManager

        status_terr = target->transact(code, *data, reply, flags);

        ......

        signalExceptionForError(env,obj, err);

        returnJNI_FALSE;

    }

    看了上面的代码会发现,Java层的Binder终于还是要借助Native的Binder进行通信的。

    说明 从架构的角度看,在Java中搭建了一整套框架,如IBinder接口,Binder类和BinderProxy类。可是从通信角度看,不论架构的编写採用的是Native语言还是Java语言,仅仅要把请求传递到Binder驱动就能够了,所以通信的目的是向binder发送请求和接收回复。

    在这个目的之上,考虑到软件的灵活性和可扩展性。于是编写了一个架构。

    反过来说,也能够不使用架构(即没有使用不论什么接口、派生之类的东西)而直接和binder交互,比如ServiceManager作为Binder的一个核心程序。就是直接读取/dev/binder设备。获取并处理请求。从这一点上看,Binder的目的虽是简单的(即打开binder设备,然后读请求和写回复)。可是架构是复杂的(编写各种接口类和封装类等)。

    我们在研究源码时。一定要先搞清楚目的。实现仅仅只是是达到该目的的一种手段和方式。脱离目的的实现,如缘木求鱼。非常easy偏离事物本质。

    在对addService进行分析时曾提示writeStrongBinder是一个特别的函数。那么它特别在哪里呢?以下将给出解释。

    (3)三人行之Binder、JavaBBinderHolder和JavaBBinder

    ActivityManagerService从ActivityManagerNative类派生,并实现了一些接口。当中和Binder的相关的仅仅有这个ActivityManagerNative类。其原型例如以下:

    [ActivityManagerNative.java-->ActivityManagerNative]

    public abstract class ActivityManagerNative

                              extends Binder

                              implementsIActivityManager

    ActivityManagerNative从Binder派生,并实现了IActivityManager接口。以下来看ActivityManagerNative的构造函数:

    [ActivityManagerNative.java-->ActivityManagerNative.ActivityManagerNative()]

    public ActivityManagerNative() {

       attachInterface(this, descriptor);// 该函数非常easy,读者可自行分析

    }

    而ActivityManagerNative父类的构造函数则是Binder的构造函数:

    [Binder.java-->Binder.Binder()]

    public Binder() {

        init();

    }

    Binder构造函数中会调用native的init函数,事实上现的代码例如以下:

    [android_util_Binder.cpp-->android_os_Binder_init()]

    static void android_os_Binder_init(JNIEnv* env,jobject obj)

    {

        // 创建一个JavaBBinderHolder对象

       JavaBBinderHolder* jbh = new JavaBBinderHolder();

       bh->incStrong((void*)android_os_Binder_init);

        // 将这个JavaBBinderHolder对象保存到Java Binder对象的mObject成员中

       env->SetIntField(obj, gBinderOffsets.mObject, (int)jbh);

    }

    从上面代码可知,Java的Binder对象将和一个Native的JavaBBinderHolder对象相关联。那么,JavaBBinderHolder是何方神圣呢?其定义例如以下:

    [android_util_Binder.cpp-->JavaBBinderHolder]

    class JavaBBinderHolder : public RefBase

    {

    public:

       sp<JavaBBinder> get(JNIEnv* env, jobject obj)

        {

           AutoMutex _l(mLock);

           sp<JavaBBinder> b = mBinder.promote();

            if(b == NULL) {

               // 创建一个JavaBBinder,obj实际上是Java层中的Binder对象

               b = new JavaBBinder(env, obj);

               mBinder = b;

            }

           return b;

        }

        ......

    private:

       Mutex           mLock;

       wp<JavaBBinder> mBinder;

    };

    从派生关系上能够发现,JavaBBinderHolder仅从RefBase派生,所以它不属于Binder家族。Java层的Binder对象为什么会和Native层的一个与Binder家族无关的对象绑定呢?细致观察JavaBBinderHolder的定义可知:JavaBBinderHolder类的get函数中创建了一个JavaBBinder对象。这个对象就是从BnBinder派生的。

    那么,这个get函数是在哪里调用的?答案在以下这句代码中:

    //当中。data是Parcel对象。service此时还是ActivityManagerService

    data.writeStrongBinder(service);

    writeStrongBinder会做一个替换工作,以下是它的native代码实现:

    [android_util_Binder.cpp-->android_os_Parcel_writeStrongBinder()]

    static voidandroid_os_Parcel_writeStrongBinder(JNIEnv* env,

                                                   jobject clazz, jobject object)

    {

        /*parcel是一个Native的对象,writeStrongBinder的真正參数是

         ibinderForJavaObject()的返回值*/

        conststatus_t err = parcel->writeStrongBinder(

                                       ibinderForJavaObject(env, object));

    }

    [android_util_Binder.cpp-->ibinderForJavaObject()]

    sp<IBinder> ibinderForJavaObject(JNIEnv*env, jobject obj)

    {

        /* 假设Java的obj是Binder类。则首先获得JavaBBinderHolder对象,然后调用

          它的get()函数。而这个get将返回一个JavaBBinder  */

        if(env->IsInstanceOf(obj, gBinderOffsets.mClass)) {

        JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetIntField(obj,

                                         gBinderOffsets.mObject);

           return jbh != NULL ? jbh->get(env, obj) : NULL;

        }

        // 假设obj是BinderProxy类,则返回Native的BpBinder对象

        if(env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {

           return (IBinder*)

               env->GetIntField(obj, gBinderProxyOffsets.mObject);

        }

       returnNULL;

    }

    依据上面的介绍会发现,addService实际加入到Parcel的并非AMS本身,而是一个叫JavaBBinder的对象。

    正是将它终于传递到Binder驱动。

    读者此时easy想到,Java层中全部的Binder相应的都是这个JavaBBinder。当然。不同的Binder对象相应不同的JavaBBinder对象。

    图2-2展示了Java Binder、JavaBBinderHolder和JavaBBinder的关系。


    图 2 - 2 Java Binder 、JavaBBinderHolder和JavaBBinder三者的关系

    从图2-2可知:

    ·  Java层的Binder通过mObject指向一个Native层的JavaBBInderHolder对象。

    ·  Native层的JavaBBinderHolder对象通过mBinder成员变量指向一个Native的JavaBBinder对象。

    ·  Native的JavaBBinder对象又通过mObject变量指向一个Java层的Binder对象。

    为什么不直接让Java层的Binder对象指向Native层的JavaBBinder对象呢?由于缺乏设计文档。这里不便妄加揣測。但从JavaBBinderHolder的实现上来分析,预计和垃圾回收(内存管理)有关,由于JavaBBinderHolder中的mBinder对象的类型被定义成弱引用wp了。

    建议 对此有更好的解释的读者,最好还是与大家分享一下。

    2. ActivityManagerService响应请求

    初见JavaBBinde时,多少有些惊讶。

    回想一下Native层的Binder架构:尽管在代码中调用的是Binder类提供的接口,但其对象却是一个实际的服务端对象,比如MediaPlayerService对象。AudioFlinger对象。

    而在Java层的Binder架构中,JavaBBinder却是一个和业务全然无关的对象。那么,这个对象怎样实现不同业务呢?

    为回答此问题,我们必须看它的onTransact函数。当收到请求时,系统会调用这个函数。

    说明 关于这个问题,建议读者阅读卷I第6章“深入理解Binder”。

    [android_util_Binder.cpp-->JavaBBinder::onTransact()]

    virtual status_t onTransact(

           uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags =0)

    {

        JNIEnv*env = javavm_to_jnienv(mVM);

       IPCThreadState* thread_state = IPCThreadState::self();

        .......

        // 调用Java层Binder对象的execTranscat函数

        jbooleanres = env->CallBooleanMethod(mObject,

                        gBinderOffsets.mExecTransact,code,

                       (int32_t)&data, (int32_t)reply, flags);

        ......

        returnres != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;

    }

    就本例而言,上面代码中的mObject就是ActivityManagerService,如今调用它的execTransact()方法,该方法在Binder类中实现。具体代码例如以下:

    [Binder.java-->Binder.execTransact()]

    private boolean execTransact(int code, intdataObj, int replyObj,int flags) {

        Parceldata = Parcel.obtain(dataObj);

        Parcelreply = Parcel.obtain(replyObj);

        booleanres;

        try {

            //调用onTransact函数,派生类能够又一次实现这个函数,以完毕业务功能

            res= onTransact(code, data, reply, flags);

        } catch{ ... }

       reply.recycle();

       data.recycle();

        returnres;

    }

    ActivityManagerNative类实现了onTransact函数。代码例如以下:

    [ActivityManagerNative.java-->ActivityManagerNative.onTransact()]

    public boolean onTransact(int code, Parcel data,Parcel reply, int flags)

               throws RemoteException {

        switch(code) {

        caseSTART_ACTIVITY_TRANSACTION:

        {

           data.enforceInterface(IActivityManager.descriptor);

           IBinder b = data.readStrongBinder();

           ......

            //再由ActivityManagerService实现业务函数startActivity

            intresult = startActivity(app, intent, resolvedType,

                       grantedUriPermissions, grantedMode, resultTo, resultWho,

                       requestCode, onlyIfNeeded, debug, profileFile,

                       profileFd, autoStopProfiler);

           reply.writeNoException();

           reply.writeInt(result);

           return true;

        }

        .... // 处理其它请求的case

        }

    }

    由此能够看出。JavaBBinder仅是一个传声筒。它本身不实现不论什么业务函数。其工作是:

    ·  当它收到请求时,仅仅是简单地调用它所绑定的Java层Binder对象的exeTransact。

    ·  该Binder对象的exeTransact调用其子类实现的onTransact函数。

    ·  子类的onTransact函数将业务又派发给其子类来完毕。

    请读者务必注意当中的多层继承关系。

    通过这样的方式。来自client的请求就能传递到正确的Java Binder对象了。

    图2-3展示AMS响应请求的整个流程。


    图 2 - 3 AMS响应请求的流程

    在图2-3中,右上角的慷慨框表示AMS这个对象,其间的虚线箭头表示调用子类重载的函数。

    2.2.4 理解AIDL

    经过上一节的介绍。读者已经明确在Java层Binder的架构中,Bp端能够通过BinderProxy的transact()方法与Bn端发送请求。而Bn端通过继承Binder类并重写onTransact()接收并处理来自Bp端的请求。

    这个结构非常清楚并且简单。可是实现起来却颇为繁琐。于是Android提供了AIDL语言以及AIDL解释器自己主动生成一个服务的Bn端即Bp端的用于处理Binder通信的代码。

    AIDL的语法与定义一个Java接口的语法非常相似。为了避免业务实现对分析的干扰,本节通过一个最简单的样例对AIDL的原理进行介绍:

    [IMyServer.aidl]

    package com.understanding.samples;

    interface IMyServer {

        intfoo(String str);

    }

    IMyServer.aidl定义了一个名为IMyServer的Binder服务,并提供了一个能够跨Binder调用的接口foo()。能够通过aidl工具将其解析为一个实现了Bn端及Bp端通过Binder进行通信的Java源码。具体命令例如以下:

    aidl com/understanding/samples/IMyServer.aidl

    生成的IMyServer.java能够在com/understanding/samples/文件夹下找到。

    建议 读者能够阅读aidl有关的文档了解此工具的具体功能。

    [IMyServer.java-->IMyServer]

    package com.understanding.samples;

    /* ① 首先。IMyServer.aidl被解析为一个Java接口IMyServer。这个接口定义了AIDL文件里

      所定义的接口foo() */

    public interface IMyServer extendsandroid.os.IInterface {

        /*② aidl工具生成了一个继承自IMyServer接口的抽象类IMyServer.Stub。这个抽象类实现了

          Bn端通过onTransact()方法接收来自Bp端的请求的代码。

    本例中的foo()方法在这个类中

          会被定义成一个抽象方法。由于aidl工具根不知道foo()方法是做什么的。它仅仅能在onTransact()

          中得知Bp端希望对foo()方法进行调用。所以Stub类是抽象的。 */

        publicstatic abstract class Stub extends android.os.Binder implements

                                               com.understanding.samples.IMyServer{

           ...... // Stub类的其它实现

            /*onTransact()依据code的值选择调用IMyServer接口中的不同方法。本例中

             TRANSACTION_foo意味着须要通过调用foo()方法完毕请求 */

           public boolean onTransact(int code, android.os.Parcel data,

                   android.os.Parcel reply, int flags)

                   throws android.os.RemoteException {

               switch (code) {

                ......

               case TRANSACTION_foo: {

                   ...... // 从data中读取參数_arg0

                   // Stub类的子类须要实现foo()方法

                    int _result = this.foo(_arg0);

                   ...... // 向reply中写入_result

                   return true;

               }

               }

               return super.onTransact(code, data, reply, flags);

            }

            /* ③ aidl工具还生成了一个继承自IMyServer接口的类Proxy,它是Bp端的实现。

    与Bn端的

             Stub类不同。它实现了foo()函数。

    由于foo()函数在Bp端的实现是确定的,即将參数存储到

             Parcel中然后运行transact()方法将请求发送给Bn端。然后从reply中读取返回值并返回

              给调用者 */

              private static class Proxy implements com.understanding.samples.IMyServer{

                ...... // Proxy类的其它实现

               public int foo(java.lang.String str)

                       throws android.os.RemoteException {

                   android.os.Parcel _data = android.os.Parcel.obtain();

                   android.os.Parcel _reply = android.os.Parcel.obtain();

                   int _result;

                   try {

                       ...... // 将參数str写入參数_data

                       // mRemote就是指向IMyServer Bn端的BinderProxy

                       mRemote.transact(Stub.TRANSACTION_foo, _data, _reply, 0);

                       ......// 从_replay中读取返回值_result

                   } finally { ...... }

                   return _result;

               }

            }

            // TRANSACTION_foo常量用于定义foo()方法的code

              static final int TRANSACTION_foo =

                                 (android.os.IBinder.FIRST_CALL_TRANSACTION+ 0);

        }

        // 声明IMyServer所提供的接口

        publicint foo(java.lang.String str) throws android.os.RemoteException;

    }

    可见一个AIDL文件被aidl工具解析之后会产生三个物件:

    ·  IMyServer接口。

    它仅仅用来在Java中声明IMyServer.aidl中所声明的接口。

    ·  IMyServer.Stub类。这个继承自Binder类的抽象类实现了Bn端与Binder通信相关的代码。

    ·  IMyServer.Stub.Proxy类。这个类实现了Bp端与Binder通信相关的代码。

    在完毕了aidl的解析之后,为了实现一个Bn端,开发人员须要继承IMyServer.Stub类并实现其抽象方法。

    例如以下所看到的:

    class MyServer extends IMyServer.Stub {

        intfoo(String str) {

            // 做点什么都能够

            returnstr.length();

        }

    }

    于是每个MyServer类的实例,都具有了作为Bn端的能力。

    典型的做法是将MyServer类的实例通过ServiceManager.addService()将其注冊为一个系统服务,或者在一个Android标准Service的onBind()方法中将其作为返回值使之能够被其它进程訪问。另外,也能够通过Binder调用将其传递给另外一个进程,使之成为一个跨进程的回调对象。

    那么Bp端将怎样使用IMyServer.Proxy呢?在Bp端所在进程中。一旦获取了IMyServer的BinderProxy(通过ServiceManager.getService()、onServiceConnected()或者其它方式)。就能够以例如以下方式获得一个IMyServer.Proxy:

    // 当中binderProxy就是通过ServiceManager.getService()所获取

    IMyServer remote = IMyServer.Stub.asInterface(binderProxy);

    remote.foo(“Hello AIDL!”);

    IMyServer.Stub.asInterface()的实现例如以下:

    [IMyServer.java-->IMyServer.Stub.asInterface()]

    public static com.understanding.samples.IMyServerasInterface(

           android.os.IBinder obj) {

        ......

        // 创建一个IMyServer.Stub.Proxy。

    当中參数obj将会被保存为Proxy类的mRemote成员。

        return new com.understanding.samples.IMyServer.Stub.Proxy(obj);

    }

    可见,AIDL使得构建一个Binder服务的工作大大地简化了。

    2.2.5 Java层Binder架构总结

    图2-4展示了Java层的Binder架构。


    图 2 - 4 Java层Binder架构

    依据图2-4可知:

    q  对于代表client的BinderProxy来说,Java层的BinderProxy在Native层相应一个BpBinder对象。凡是从Java层发出的请求,首先从Java层的BinderProxy传递到Native层的BpBinder,继而由BpBinder将请求发送到Binder驱动。

    q  对于代表服务端的Service来说,Java层的Binder在Native层有一个JavaBBinder对象。

    前面介绍过,全部Java层的Binder在Native层都相应为JavaBBinder。而JavaBBinder仅起到中转作用。即把来自client的请求从Native层传递到Java层。

    q  系统中依旧仅仅有一个Native的ServiceManager。

    至此,Java层的Binder架构已介绍完毕。从前面的分析能够看出,Java层Binder非常依赖Native层的Binder。

    建议想进一步了解Binder的读者们,要深入了解这一问题,有必要阅读卷I的第6章“深入理解Binder”。

    2.3 心系两界的MessageQueue

    卷I第5章介绍过。MessageQueue类封装了与消息队列有关的操作。

    在一个以消息驱动的系统中。最重要的两部分就是消息队列和消息处理循环。在Andrid 2.3曾经,仅仅有Java世界的居民有资格向MessageQueue中加入消息以驱动Java世界的正常运转,但从Android 2.3開始,MessageQueue的核心部分下移至Native层,让Native世界的居民也能利用消息循环来处理他们所在世界的事情。

    因此如今的MessageQueue心系Native和Java两个世界。

    2.3.1 MessageQueue的创建

    如今来分析MessageQueue是怎样跨界工作的。其代码例如以下:

    [MessageQueue.java-->MessageQueue.MessageQueue()]

    MessageQueue() {

        nativeInit();//构造函数调用nativeInit,该函数由Native层实现

    }

    nativeInit()方法的真正实现为android_os_MessageQueue_nativeInit()函数,其代码例如以下:

    [android_os_MessageQueue.cpp-->android_os_MessageQueue_nativeInit()]

    static voidandroid_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {

        // NativeMessageQueue是MessageQueue在Native层的代表

        NativeMessageQueue* nativeMessageQueue = newNativeMessageQueue();

        ......

        // 将这个NativeMessageQueue对象设置到Java层保存

        android_os_MessageQueue_setNativeMessageQueue(env,obj,

                                                             nativeMessageQueue);

    }

    nativeInit函数在Native层创建了一个与MessageQueue相应的NativeMessageQueue对象,其构造函数例如以下:

    [android_os_MessageQueue.cpp-->NativeMessageQueue::NativeMessageQueue()]

    NativeMessageQueue::NativeMessageQueue() {

        /* 代表消息循环的Looper也在Native层中呈现身影了。依据消息驱动的知识,一个线程会有一个

          Looper来循环处理消息队列中的消息。以下一行的调用就是取得保存在线程本地存储空间

         (Thread Local Storage)中的Looper对象 */

        mLooper= Looper::getForThread();

        if (mLooper == NULL) {

            /* 如为第一次进来,则该线程没有设置本地存储,所以须先创建一个Looper,然后再将其保存到

              TLS中,这是非经常见的一种以线程为单位的单例模式*/

            mLooper = new Looper(false);

            Looper::setForThread(mLooper);

        }

    }

    Native的Looper是Native世界中參与消息循环的一位重要角色。

    尽管它的类名和Java层的Looper类一样,但此二者事实上并无不论什么关系。

    这一点以后还将具体分析。

    2.3.2 提取消息

    当一切准备就绪后,Java层的消息循环处理,也就是Looper会在一个循环中提取并处理消息。消息的提取就是调用MessageQueue的next()方法。当消息队列为空时。next就会堵塞。MessageQueue同一时候支持Java层和Native层的事件,那么其next()方法该怎么实现呢?具体代码例如以下:

    [MessagQueue.java-->MessageQueue.next()]

    final Message next() {

        int pendingIdleHandlerCount = -1;

        int nextPollTimeoutMillis = 0;

        for (;;) {

            ......

            // mPtr保存了NativeMessageQueue的指针。调用nativePollOnce进行等待

            nativePollOnce(mPtr,nextPollTimeoutMillis);

            synchronized (this) {

                final long now = SystemClock.uptimeMillis();

                // mMessages用来存储消息,这里从当中取一个消息进行处理

                final Message msg = mMessages;

                if (msg != null) {

                    final long when = msg.when;

                    if (now >= when) {

                        mBlocked = false;

                        mMessages = msg.next;

                        msg.next = null;

                        msg.markInUse();

                        return msg; // 返回一个Message给Looper进行派发和处理

                   } else {

                        nextPollTimeoutMillis = (int) Math.min(when- now,

                                                      Integer.MAX_VALUE);

                    }

                } else {

                    nextPollTimeoutMillis = -1;

                }

                ......

                /* 处理注冊的IdleHandler,当MessageQueue中没有Message时,

               Looper会调用IdleHandler做一些工作,比如做垃圾回收等  */

               ......

               pendingIdleHandlerCount = 0;

                nextPollTimeoutMillis = 0;

            }

        }

    }

    看到这里,可能会有人觉得这个MessageQueue非常easy,不就是从曾经在Java层的wait变成如今Native层的wait了吗?可是事情本质比表象要复杂得多,来思考以下的情况:

    nativePollOnce()返回后,next()方法将从mMessages中提取一个消息。

    也就是说。要让nativePollOnce()返回,至少要加入一个消息到消息队列。否则nativePollOnce()只是是做了一次无用功罢了。

    假设nativePollOnce()将在Native层等待,就表明Native层也能够投递Message,可是从Message类的实现代码上看,该类和Native层没有建立不论什么关系。

    那么nativePollOnce()在等待什么呢?

    对于上面的问题,相信有些读者心中已有了答案:nativePollOnce()不仅在等待Java层来的Message,实际上还在Native还做了大量的工作。

    以下我们来分析Java层投递Message并触发nativePollOnce工作的正常流程。

    1. 在Java层投递Message

    MessageQueue的enqueueMessage函数完毕将一个Message投递到MessageQueue中的工作,其代码例如以下:

    [MesssageQueue.java-->MessageQueue.enqueueMessage()]

    final boolean enqueueMessage(Message msg, longwhen) {

        ......

        finalboolean needWake;

       synchronized (this) {

            if(mQuiting) {

               return false;

            }else if (msg.target == null) {

               mQuiting = true;

            }

           msg.when = when;

           Message p = mMessages;

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

               /* 假设p为空,表明消息队列中没有消息,那么msg将是第一个消息,needWake

                 须要依据mBlocked的情况考虑是否触发 */

                msg.next= p;

               mMessages = msg;

               needWake = mBlocked;

            } else {

               // 假设p不为空,表明消息队列中还有剩余消息,须要将新的msg加到消息尾

               Message prev = null;

               while (p != null && p.when <= when) {

                   prev = p;

                   p = p.next;

               }

               msg.next = prev.next;

               prev.next = msg;

               // 由于消息队列之前还剩余有消息,所以这里不用调用nativeWakeup

               needWake = false;

            }

        }

        if(needWake) {

            // 调用nativeWake,以触发nativePollOnce函数结束等待

           nativeWake(mPtr);

        }

        returntrue;

    }

    上面的代码比較简单。主要功能是:

    ·  将message按运行时间排序。并加入消息队。

    ·  依据情况调用nativeWake函数,以触发nativePollOnce函数。结束等待。

    建议 尽管代码简单。可是对于那些不熟悉多线程的读者,还是要细细品味一下mBlocked值的作用。我们常说细节体现美,代码也一样,这个小小的mBlocked正是如此。

    2. nativeWake函数分析

    nativeWake函数的代码例如以下所看到的:

    [android_os_MessageQueue.cpp-->android_os_MessageQueue_nativeWake()]

    static voidandroid_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj,

                                                          jint ptr)

    {

       NativeMessageQueue* nativeMessageQueue = // 取出NativeMessageQueue对象

                          reinterpret_cast<NativeMessageQueue*>(ptr);

        returnnativeMessageQueue->wake(); // 调用它的wake函数

    }

    [android_os_MessageQueue.cpp-->NativeMessageQueue::wake()]

    void NativeMessageQueue::wake() {

       mLooper->wake(); // 层层调用,如今转到mLooper的wake函数

    }

    Native Looper的wake函数代码例如以下:

    [Looper.cpp-->Looper::wake()]

    void Looper::wake() {

        ssize_tnWrite;

        do {

            // 向管道的写端写入一个字符

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

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

    }

    Wake()函数则更为简单,仅仅向管道的写端写入一个字符”W”,这样管道的读端就会由于有数据可读而从等待状态中醒来。

    2.3.3 nativePollOnce函数分析

    nativePollOnce()的实现函数是android_os_MessageQueue_nativePollOnce。代码例如以下:

    [android_os_MessageQueue.cpp-->android_os_MessageQueue_nativePollOnce()]

    static voidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,

            jintptr, jint timeoutMillis)

        NativeMessageQueue* nativeMessageQueue =

                               reinterpret_cast<NativeMessageQueue*>(ptr);

        // 取出NativeMessageQueue对象,并调用它的pollOnce

       nativeMessageQueue->pollOnce(timeoutMillis);

    }

    分析pollOnce函数:

    [android_os_MessageQueue.cpp-->NativeMessageQueue::pollOnece()]

    void NativeMessageQueue::pollOnce(inttimeoutMillis) {

       mLooper->pollOnce(timeoutMillis); // 重任传递到Looper的pollOnce函数

    }

    Looper的pollOnce函数例如以下:

    [Looper.cpp-->Looper::pollOnce()]

    inline int pollOnce(int timeoutMillis) {

        returnpollOnce(timeoutMillis, NULL, NULL, NULL);

    }

    上面的函数将调用另外一个有4个參数的pollOnce函数,这个函数的原型例如以下:

    int pollOnce(int timeoutMillis, int* outFd, int*outEvents, void** outData)

    当中:

    ·  timeOutMillis參数为超时等待时间。假设为-1,则表示无限等待,直到有事件发生为止。假设值为0。则无需等待马上返回。

    ·  outFd用来存储发生事件的那个文件描写叙述符 。

    ·  outEvents用来存储在该文件描写叙述符1上发生了哪些事件,眼下支持可读、可写、错误和中断4个事件。这4个事件事实上是从epoll事件转化而来。后面我们会介绍大名鼎鼎的epoll。

    ·  outData用于存储上下文数据,这个上下文数据是由用户在加入监听句柄时传递的,它的作用和pthread_create函数最后一个參数param一样。用来传递用户自己定义的数据。

    另外。pollOnce函数的返回值也具有特殊的意义,具体例如以下:

    ·  当返回值为ALOOPER_POLL_WAKE时,表示这次返回是由wake函数触发的,也就是管道写端的那次写事件触发的。

    ·  返回值为ALOOPER_POLL_TIMEOUT表示等待超时。

    ·  返回值为ALOOPER_POLL_ERROR,表示等待过程中错误发生。

    ·  返回值为ALOOPER_POLL_CALLBACK,表示某个被监听的句柄因某种原因被触发。

    这时。outFd參数用于存储发生事件的文件句柄,outEvents用于存储所发生的事件。

    上面这些知识是和epoll息息相关的。

    提示 查看Looper的代码会发现,Looper採用了编译选项(即#if和#else)来控制是否使用epoll作为I/O复用的控制中枢。

    鉴于如今大多数系统都支持epoll,这里仅讨论使用epoll的情况。

    1. epoll基础知识介绍

    epoll机制提供了Linux平台上最高效的I/O复用机制,因此有必要介绍一下它的基础知识。

    从调用方法上看,epoll的使用方法和select/poll非常相似。其主要作用就是I/O复用。即在一个地方等待多个文件句柄的I/O事件。

    以下通过一个简单样例来分析epoll的工作流程。

    /* ① 使用epoll前。须要先通过epoll_create函数创建一个epoll句柄。

      以下一行代码中的10表示该epoll句柄初次创建时候分配能容纳10个fd相关信息的缓存。

      对于2.6.8版本号以后的内核,该值没有实际作用,这里能够忽略。事实上这个值的主要目的是

      确定分配一块多大的缓存。

    如今的内核都支持动态拓展这块缓存,所以该值就没有意义了 */

    int epollHandle = epoll_create(10);

    /* ② 得到epoll句柄后,下一步就是通过epoll_ctl把须要监听的文件句柄加入到epoll句柄中。

      除了指定文件句柄本身的fd值外,同一时候还须要指定在该fd上等待什么事件。

    epoll支持四类事件,

      各自是EPOLLIN(句柄可读)、EPOLLOUT(句柄可写),EPOLLERR(句柄错误)、EPOLLHUP(句柄断)。

      epoll定义了一个结构体struct epoll_event来表达监听句柄的诉求。

      假设如今有一个监听端的socket句柄listener,要把它加入到epoll句柄中 */

    struct epoll_event listenEvent; //先定义一个event

    /* EPOLLIN表示可读事件,EPOLLOUT表示可写事件。另外还有EPOLLERR,EPOLLHUP表示

      系统默认会将EPOLLERR加入到事件集合中 */

    listenEvent.events = EPOLLIN;// 指定该句柄的可读事件

    // epoll_event中有一个联合体叫data。用来存储上下文数据。本例的上下文数据就是句柄自己

    listenEvent.data.fd = listenEvent;

      /* EPOLL_CTL_ADD将监听fd和监听事件加入到epoll句柄的等待队列中;

        EPOLL_CTL_DEL将监听fd从epoll句柄中移除。

        EPOLL_CTL_MOD改动监听fd的监听事件,比如本来仅仅等待可读事件,如今须要同一时候等待

        可写事件,那么改动listenEvent.events 为EPOLLIN|EPOLLOUT后,再传给epoll句柄*/

    epoll_ctl(epollHandle,EPOLL_CTL_ADD,listener,&listenEvent);

    /* 当把全部感兴趣的fd都加入到epoll句柄后。就能够開始坐等感兴趣的事情发生了。

      为了接收所发生的事情,先定义一个epoll_event数组 */

    struct epoll_event resultEvents[10];

    int timeout = -1;

    while(1) {

        /* ④ 调用epoll_wait用于等待事件。当中timeout能够指定一个超时时间,

         resultEvents用于接收发生的事件,10为该数组的大小。

         epoll_wait函数的返回值有例如以下含义:

          nfds大于0表示所监听的句柄上有事件发生;

          nfds等于0表示等待超时;

          nfds小于0表示等待过程中发生了错误*/

        int nfds = epoll_wait(epollHandle,resultEvents, 10, timeout);

        if(nfds == -1) {

            // epoll_wait发生了错误

        } else if(nfds == 0) {

            //发生超时,期间没有发生不论什么事件

        } else{

            // ⑤resultEvents用于返回那些发生了事件的信息

            for(int i = 0; i < nfds; i++) {

                struct epoll_event & event =resultEvents[i];

                if(event & EPOLLIN) {

                    /* ⑥ 收到可读事件。究竟是哪个文件句柄发生该事件呢?可通过event.data这个联合

                     体取得 前传递给epoll的上下文数据,该上下文信息可用于推断究竟是谁发生了事件 */

                  ......

                }

                .......//其它处理 

            }

        }

    }

    epoll总体使用流程如上面代码所看到的,基本和select/poll相似,只是作为Linux平台最高效的I/O复用机制。这里有些内容供读者參考。

    epoll的效率为什么会比select高?当中一个原因是调用方法。每次调用select时,都须要把感兴趣的事件拷贝到内核中。而epoll仅仅在epll_ctl进行加入的时候复制一次。另外。epoll内部用于保存事件的数据结构使用的是红黑树,查找速度非常快。

    而select採用数组保存信息,不但一次能等待的句柄个数有限。并且查找起来速度非常慢。当然,在仅仅等待少量文件句柄时。select和epoll效率相差不是非常多,但还是推荐使用epoll。

    epoll等待的事件有两种触发条件,一个是水平触发(EPOLLLEVEL),另外一个是边缘触发(EPOLLET,ET为Edge Trigger之意),这两种触发条件的差别非常重要。读者可通过man epoll查阅系统提供的更为具体的epoll机制。

    最后,关于pipe,还想提出一个小问题供读者思考讨论:

    为什么Android中使用pipe作为线程间通讯的方式?对于pipe的写端写入的数据,读端都不感兴趣。仅仅是为了简单的唤醒。POSIX不是也有线程间同步函数吗?为什么要用pipe呢?

    关于这个问题的答案,可參见邓凡平的一篇博文“随笔之怎样实现一个线程池”。

    ·  http://www.cnblogs.com/innost/archive/2011/11/24/2261454.html

    2. pollOnce()函数分析

    以下分析带4个參数的pollOnce()函数。代码例如以下:

    [Looper.cpp-->Looper::pollOnce()]

    int Looper::pollOnce(int timeoutMillis, int*outFd, int* outEvents,

    void** outData) {

        intresult = 0;

        for(;;) { // 一个无限循环

            // mResponses是一个Vector,这里首先须要处理response

           while (mResponseIndex < mResponses.size()) {

               const Response& response = mResponses.itemAt(mResponseIndex++);

               ALooper_callbackFunc callback = response.request.callback;

                if (!callback) {// 首先处理那些没有callback的Response

                   int ident = response.request.ident; // ident是这个Response的id

                   int fd = response.request.fd;

                   int events = response.events;

                   void* data = response.request.data;

                   ......

                   if (outFd != NULL) *outFd = fd;

                   if (outEvents != NULL) *outEvents = events;

                   if (outData != NULL) *outData = data;

                   /* 实际上,对于没有callback的Response。pollOnce仅仅是返回它的

                     ident,并没有实际做什么处理。由于没有callback。所以系统也不知道怎样处理 */

                   return ident;

               }

            }

            if(result != 0) {

                if(outFd != NULL) *outFd = 0;

               if (outEvents != NULL) *outEvents = NULL;

                if (outData != NULL) *outData = NULL;

               return result;

            }

            // 调用pollInner函数。

    注意,它在for循环内部

           result = pollInner(timeoutMillis);

        }

    }

    初看上面的代码,可能会让人有些丈二和尚摸不着头脑。

    可是把pollInner()函数分析完毕,大家就会明确非常多。

    pollInner()函数非常长。把用于调试和统计的代码去掉。结果例如以下:

    [Looper.cpp-->Looper::pollInner()]

    int Looper::pollInner(int timeoutMillis) {

        if(timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {

           nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

           ......//依据Native Message的信息计算此次须要等待的时间

           timeoutMillis = messageTimeoutMillis;

        }

        intresult = ALOOPER_POLL_WAKE;

       mResponses.clear();

       mResponseIndex = 0;

    #ifdef LOOPER_USES_EPOLL  // 仅仅讨论使用epoll进行I/O复用的方式

        structepoll_event eventItems[EPOLL_MAX_EVENTS];

        // 调用epoll_wait,等待感兴趣的事件或超时发生

        inteventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS,

                                         timeoutMillis);

    #else

        ......//使用别的方式进行I/O复用

    #endif

        //从epoll_wait返回,这时候一定发生了什么事情

       mLock.lock();

        if(eventCount < 0) { //返回值小于零,表示错误发生

            if(errno == EINTR) {

               goto Done;

            }

            //设置result为ALLOPER_POLL_ERROR,并跳转到Done

           result = ALOOPER_POLL_ERROR;

            gotoDone;

        }

       //eventCount为零,表示发生超时,因此直接跳转到Done

        if(eventCount == 0) {

          result = ALOOPER_POLL_TIMEOUT;

            gotoDone;

        }

    #ifdef LOOPER_USES_EPOLL

        // 依据epoll的使用方法。此时的eventCount表示发生事件的个数

        for (inti = 0; i < eventCount; i++) {

            intfd = eventItems[i].data.fd;

           uint32_t epollEvents = eventItems[i].events;

            /* 之前通过pipe函数创建过两个fd,这里依据fd知道是管道读端有可读事件。

             读者还记得对nativeWake函数的分析吗?在那里我们向管道写端写了一个”W”字符,这样

             就能触发管道读端从epoll_wait函数返回了 */

            if(fd == mWakeReadPipeFd) {

               if (epollEvents & EPOLLIN) {

                    // awoken函数直接读取并清空管道数据。读者可自行研究该函数

                   awoken();

               }

                ......

            }else {

              /* mRequests和前面的mResponse相相应,它也是一个KeyedVector,当中存储了

               fd和相应的Request结构体,该结构体封装了和监控文件句柄相关的一些上下文信息,

                比如回调函数等。我们在后面的小节会再次介绍该结构体 */

               ssize_t requestIndex = mRequests.indexOfKey(fd);

               if (requestIndex >= 0) {

                   int events = 0;

                   // 将epoll返回的事件转换成上层LOOPER使用的事件

                   if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;

                   if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;

                   if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;

                   if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;

                   // 每处理一个Request,就相应构造一个Response

                   pushResponse(events, mRequests.valueAt(requestIndex));

               } 

                ......

            }

        }

    Done: ;

    #else

         ......

    #endif

        // 除了处理Request外,还处理Native的Message

       mNextMessageUptime = LLONG_MAX;

        while(mMessageEnvelopes.size() != 0) {

           nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

           const MessageEnvelope& messageEnvelope =mMessageEnvelopes.itemAt(0);

            if(messageEnvelope.uptime <= now) {

               {

                   sp<MessageHandler> handler = messageEnvelope.handler;

                   Message message = messageEnvelope.message;

                   mMessageEnvelopes.removeAt(0);

                   mSendingMessage = true;

                   mLock.unlock();

                   /* 调用Native的handler处理Native的Message

                    从这里也可看出Native Message和Java层的Message没有什么关系 */

                   handler->handleMessage(message);

               }

               mLock.lock();

               mSendingMessage = false;

               result = ALOOPER_POLL_CALLBACK;

            }else {

                mNextMessageUptime = messageEnvelope.uptime;

               break;

            }

        }

       mLock.unlock();

        // 处理那些带回调函数的Response

        for (size_t i = 0; i < mResponses.size();i++) {

           const Response& response = mResponses.itemAt(i);

           ALooper_callbackFunc callback = response.request.callback;

            if(callback) {// 有了回调函数。就能知道怎样处理所发生的事情了

               int fd = response.request.fd;

               int events = response.events;

               void* data = response.request.data;

               // 调用回调函数处理所发生的事件

               int callbackResult = callback(fd, events, data);

               if (callbackResult == 0) {

                   // callback函数的返回值非常重要。假设为0。表明不须要再次监视该文件句柄

                    removeFd(fd);

               }

               result = ALOOPER_POLL_CALLBACK;

            }

        }

        returnresult;

    }

    看完代码了,是否还有点模糊?那么,回想一下pollInner函数的几个关键点:

    ·  首先须要计算一下真正须要等待的时间。

    ·  调用epoll_wait函数等待。

    ·  epoll_wait函数返回,这时候可能有三种情况:

    a)      错误发生,则跳转到Done处。

    b)     超时。这时候也跳转到Done处。

    c)      epoll_wait监測到某些文件句柄上有事件发生。

    ·  假设epoll_wait由于文件句柄有事件而返回,此时须要依据文件句柄来分别处理:

    a)      假设是管道读这一端有事情,则觉得是控制命令。能够直接读取管道中的数据。

    b)     假设是其它FD发生事件,则依据Request构造Response,并push到Response数组中。

    ·  真正開始处理事件是在有Done标志的位置。

    a)      首先处理Native的Message。调用Native Handler的handleMessage处理该Message。

    b)     处理Response数组中那些带有callback的事件。

    上面的处理流程还是比較清楚的。但还是有个一个拦路虎,那就是mRequests,以下就来清剿这个拦路虎。

    3. 加入监控请求

    加入监控请求事实上就是调用epoll_ctl添加文件句柄。以下通过从Native的Activity找到的一个样例来分析mRequests。

    [android_app_NativeActivity.cpp-->loadNativeCode_native()]

    static jint

    loadNativeCode_native(JNIEnv* env, jobject clazz,jstring path,

                              jstringfuncName,jobject messageQueue,

                              jstringinternalDataDir, jstring obbDir,

                              jstringexternalDataDir, int sdkVersion,

                              jobject jAssetMgr,jbyteArray savedState)

    {

        ......

        /* 调用Looper的addFd函数。第一个參数表示监听的fd;第二个參数0表示ident;

          第三个參数表示须要监听的事件,这里为仅仅监听可读事件。第四个參数为回调函数,当该fd发生

          指定事件时。looper将回调该函数;第五个參数code为回调函数的參数 */

        code->looper->addFd(code->mainWorkRead,0,

                              ALOOPER_EVENT_INPUT,mainWorkCallback, code);

      ......

    }

    Looper的addFd()代码例如以下所看到的:

    [Looper.cpp-->Looper::addFd()]

    int Looper::addFd(int fd, int ident, int events,

                          ALooper_callbackFunccallback, void* data) {

        if (!callback) {

             /* 推断该Looper是否支持不带回调函数的文件句柄加入。

    一般不支持,由于没有回调函数

               Looper也不知道怎样处理该文件句柄上发生的事情 */

             if(! mAllowNonCallbacks) {

               return -1;

            }

          ......

        }

    #ifdef LOOPER_USES_EPOLL

        intepollEvents = 0;

        // 将用户的事件转换成epoll使用的值

        if(events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;

        if(events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;

        {

           AutoMutex _l(mLock);

           Request request; // 创建一个Request对象

           request.fd = fd; // 保存fd

           request.ident = ident; // 保存id

           request.callback = callback; //保存callback

           request.data = data;  // 保存用户自己定义数据

           struct epoll_event eventItem;

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

           eventItem.events = epollEvents;

           eventItem.data.fd = fd;

            // 推断该Request是否已经存在,mRequests以fd作为key值

           ssize_t requestIndex = mRequests.indexOfKey(fd);

            if(requestIndex < 0) {

               // 假设是新的文件句柄,则须要为epoll添加该fd

               int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem);

               ......

               // 保存Request到mRequests键值数组

               mRequests.add(fd, request);

            }else {

               // 假设之前加过。那么就改动该监听句柄的一些信息

               int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, &eventItem);

              ......

               mRequests.replaceValueAt(requestIndex, request);

            }

        }

    #else

        ......

    #endif

        return1;

    }

    4. 处理监控请求

    我们发如今pollInner()函数中,当某个监控fd上发生事件后,就会把相应的Request取出来调用。

    pushResponse(events, mRequests.itemAt(i));

    此函数例如以下:

    [Looper.cpp-->Looper::pushResponse()]

    void Looper::pushResponse(int events, constRequest& request) {

        Responseresponse;

       response.events = events;

       response.request = request; //事实上非常easy。就是保存所发生的事情和相应的Request

        mResponses.push(response);//然后保存到mResponse数组

    }

    依据前面的知识可知。并非单独处理Request,而是须要先收集Request,等到Native Message消息处理完之后再做处理。这表明,在处理逻辑上,Native Message的优先级高于监控FD的优先级。

    以下来了解怎样加入Native的Message。

    5. Native的sendMessage

    Android 2.2中仅仅有Java层才干够通过sendMessage()往MessageQueue中加入消息,从4.0開始。Native层也支持sendMessage()了。sendMessage()的代码例如以下:

    [Looper.cpp-->Looper::sendMessage()]

    void Looper::sendMessage(constsp<MessageHandler>& handler,

                                  constMessage& message) {

        //Native的sendMessage函数必须同一时候传递一个Handler

        nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);

       sendMessageAtTime(now, handler, message); //调用sendMessageAtTime

    }

    [Looper.java-->Looper::sendMessageAtTime()]

    void Looper::sendMessageAtTime(nsecs_t uptime,

                                         constsp<MessageHandler>& handler,

                                         constMessage& message) {

       size_t i= 0;

        {

           AutoMutex _l(mLock);

           size_t messageCount = mMessageEnvelopes.size();

            // 按时间排序,将消息插入到正确的位置上

           while (i < messageCount &&

                   uptime >= mMessageEnvelopes.itemAt(i).uptime) {

               i += 1;

            }

           MessageEnvelope messageEnvelope(uptime, handler, message);

           mMessageEnvelopes.insertAt(messageEnvelope, i, 1);

            // mSendingMessage和Java层中的那个mBlocked一样,是一个小小的优化措施

            if(mSendingMessage) {

               return;

            }

        }

        // 唤醒epoll_wait。让它处理消息

        if (i ==0) {

           wake();

        }

    }

    2.3.4 MessageQueue总结

    想不到,一个小小的MessageQueue居然有如此多的内容。

    在后面分析Android输入系统时。会再次在Native层和MessageQueue碰面。这里仅是为后面的相会打下一定的基础。

    如今将站在一个比具体代码更高的层次来认识一下MessageQueue和它的伙伴们。

    1. 消息处理的大家族合照

    MessageQueue仅仅是消息处理大家族的一员。该家族的成员合照如图2-5所看到的。


    图 2 - 5 消息处理的家族合照

    结合前述内容可从图2-5中得到:

    ·  Java层提供了Looper类和MessageQueue类,当中Looper类提供循环处理消息的机制。MessageQueue类提供一个消息队列。以及插入、删除和提取消息的函数接口。

    另外。Handler也是在Java层经常使用的与消息处理相关的类。

    ·  MessageQueue内部通过mPtr变量保存一个Native层的NativeMessageQueue对象,mMessages保存来自Java层的Message消息。

    ·  NativeMessageQueue保存一个native的Looper对象,该Looper从ALooper派生,提供pollOnce和addFd等函数。

    ·  Java层有Message类和Handler类。而Native层相应也有Message类和MessageHandler抽象类。在编码时,一般使用的是MessageHandler的派生类WeakMessageHandler类。

    注意 在include/media/stagfright/foundation文件夹下也定义了一个ALooper类。它是供stagefright使用的相似Java消息循环的一套基础类。这样的同名类的产生,预计是两个事先未做交流的Group的人写的。

    2. MessageQueue处理流程总结

    ·  MessageQueue核心逻辑下移到Native层后,极大地拓展了消息处理的范围,总结一下有以下几点:

    ·  MessageQueue继续支持来自Java层的Message消息。也就是早期的Message加Handler的处理方式。

    ·  MessageQueue在Native层的代表NativeMessageQueue支持来自Native层的Message,是通过Native的Message和MessageHandler来处理的。

    ·  NativeMessageQueue还处理通过addFd加入的Request。在后面分析输入系统时,还会大量碰到这样的方式。

    ·  从处理逻辑上看,先是Native的Message。然后是Native的Request,最后才是Java的Message。

    2.4 本章小结

    本章先对Java层的Binder架构做了一次较为深入的分析。

    Java层的Binder架构和Native层Binder架构相似。可是Java的Binder在通信上还是依赖Native层的Binder。建议想进一步了解Native Binder工作原理的读者。阅读卷I第6章“深入理解Binder”。另外,本章还对MessageQueue进行了较为深入的分析。Android 2.2中那个功能简单的MessageQueue如今变得复杂了,原因是该类的核心逻辑下移到Native层。导致如今的MessageQueue除了支持Java层的Message派发外。还新增了支持Native Message派发以及处理来自所监控的文件句柄的事件。

  • 相关阅读:
    C++ assert()断言
    libcurl API:CURLOPT_REFERER的用法
    hdu 2821 Pusher (dfs)
    快速找到跟踪其他session产生的trc文件
    10635
    pat 1055 区间前k个
    闲话Cache:始篇
    闲话缓存:算法概述
    instance 怎么获得自己的 Metadata
    通过 dhcp-agent 访问 Metadata
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6768498.html
Copyright © 2011-2022 走看看