<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 第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所示。 ![](https://box.kancloud.cn/2016-03-01_56d567b12c31b.png) 圖 2 - 1 Java層中的Binder家族 由圖2-1可知: + 系統定義了一個IBinder接口類以及DeathRecepient接口。 + Binder類和BinderProxy類分別實現了IBinder接口。其中Binder類作為服務端的Bn的代表,而BinderProxy作為客戶端的Bp的代表。 + 系統中還定義一個BinderInternal類。該類是一個僅供Binder框架使用的類。它內部有一個GcWatcher類,該類專門用于處理和Binder相關的垃圾回收。 + Java層同樣提供一個用于承載通信數據的Parcel類。 注意 IBinder接口類中定義了一個叫FLAG_ONEWAY的整型,該變量的意義非常重要。當客戶端利用Binder機制發起一個跨進程的函數調用時,調用方(即客戶端)一般會阻塞,直到服務端返回結果。這種方式和普通的函數調用是一樣的。但是在調用Binder函數時,在指明了FLAG_ONEWAY標志后,調用方只要把請求發送到Binder驅動即可返回,而不用等待服務端的結果,這就是一種所謂的非阻塞方式。在Native層中,涉及的Binder調用基本都是阻塞的,但是在Java層的framework中,使用FLAG_ONEWAY進行Binder調用的情況非常多,以后經常會碰到。 思考 使用FLAG_ONEWAY進行函數調用的程序在設計上有什么特點?這里簡單分析一下:對于使用FLAG_ONEWAY的函數來說,客戶端僅向服務端發出了請求,但是并不能確定服務端是否處理了該請求。所以,客戶端一般會向服務端注冊一個回調(同樣是跨進程的Binder調用),一旦服務端處理了該請求,就會調用此回調來通知客戶端處理結果。當然,這種回調函數也大多采用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) &lt; 0) ???????return -1; ??? // 初始化Java BinderInternal類和Native層的關系 ??? if(int_register_android_os_BinderInternal(env) &lt; 0) ???????return -1; ??? // 初始化Java BinderProxy類和Native層的關系 ??? if(int_register_android_os_BinderProxy(env) &lt; 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,"&lt;init&gt;", "()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如何響應客戶端的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&lt;IBinder&gt; 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&lt;IBinder&gt;& 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&lt;DeathRecipientList&gt; drl = new DeathRecipientList; ???????drl->incStrong((void*)javaObjectForIBinder); ??????? //將死亡通知list和BinderProxy對象聯系起來 ???????env->SetIntField(object, gBinderProxyOffsets.mOrgue, ????????????????????????????reinterpret_cast&lt;jint&gt;(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設備,然后讀請求和寫回復),但是架構是復雜的(編寫各種接口類和封裝類等)。我們在研究源碼時,一定要先搞清楚目的。實現只不過是達到該目的的一種手段和方式。脫離目的的實現,如緣木求魚,很容易偏離事物本質。 在對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);// 該函數很簡單,讀者可自行分析 } ``` 而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&lt;JavaBBinder&gt; get(JNIEnv* env, jobject obj) ??? { ???????AutoMutex _l(mLock); ???????sp&lt;JavaBBinder&gt; b = mBinder.promote(); ??????? if(b == NULL) { ???????????// 創建一個JavaBBinder,obj實際上是Java層中的Binder對象 ???????????b = new JavaBBinder(env, obj); ???????????mBinder = b; ??????? } ???????return b; ??? } ??? ...... private: ???Mutex?????????? mLock; ???wp&lt;JavaBBinder&gt; 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&lt;IBinder&gt; 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驅動。 讀者此時容易想到,Java層中所有的Binder對應的都是這個JavaBBinder。當然,不同的Binder對象對應不同的JavaBBinder對象。 圖2-2展示了Java Binder、JavaBBinderHolder和JavaBBinder的關系。 ![](https://box.kancloud.cn/2016-03-01_56d567b1441a4.png) 圖 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函數將業務又派發給其子類來完成。請讀者務必注意其中的多層繼承關系。 通過這種方式,來自客戶端的請求就能傳遞到正確的Java Binder對象了。圖2-3展示AMS響應請求的整個流程。 ![](https://box.kancloud.cn/2016-03-01_56d567b15363e.png) 圖 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架構。 ![](https://box.kancloud.cn/2016-03-01_56d567b16b889.png) 圖 2 - 4 Java層Binder架構 根據圖2-4可知: + 對于代表客戶端的BinderProxy來說,Java層的BinderProxy在Native層對應一個BpBinder對象。凡是從Java層發出的請求,首先從Java層的BinderProxy傳遞到Native層的BpBinder,繼而由BpBinder將請求發送到Binder驅動。 + 對于代表服務端的Service來說,Java層的Binder在Native層有一個JavaBBinder對象。前面介紹過,所有Java層的Binder在Native層都對應為JavaBBinder,而JavaBBinder僅起到中轉作用,即把來自客戶端的請求從Native層傳遞到Java層。 + 系統中依然只有一個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 &gt;= 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很簡單,不就是從以前在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 &lt; p.when) { ???????????/* 如果p為空,表明消息隊列中沒有消息,那么msg將是第一個消息,needWake ?????????????需要根據mBlocked的情況考慮是否觸發 */ ??????????? msg.next= p; ???????????mMessages = msg; ???????????needWake = mBlocked; ?? ?????} else { ???????????// 如果p不為空,表明消息隊列中還有剩余消息,需要將新的msg加到消息尾 ???????????Message prev = null; ???????????while (p != null && p.when &lt;= 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&lt;NativeMessageQueue*&gt;(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&lt;NativeMessageQueue*&gt;(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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &gt;= 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 &lt;= now) { ???????????{ ???????????????sp&lt;MessageHandler&gt; 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 &lt; 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 &lt; 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; //其實很簡單,就是保存所發生的事情和對應的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&lt;MessageHandler&gt;& 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&lt;MessageHandler&gt;& handler, ???????????????????????????????????? constMessage& message) { ?? size_t i= 0; ??? { ???????AutoMutex _l(mLock); ???????size_t messageCount = mMessageEnvelopes.size(); ??????? // 按時間排序,將消息插入到正確的位置上 ???????while (i &lt; messageCount && ???????????????uptime &gt;= 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所示。 ![](https://box.kancloud.cn/2016-03-01_56d567b17d0da.png) 圖 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派發以及處理來自所監控的文件句柄的事件。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看