1. 程式人生 > 實用技巧 >[深入理解Android卷二 全文-第二章]深入理解Java Binder和MessageQueue

[深入理解Android卷二 全文-第二章]深入理解Java Binder和MessageQueue

>>> hot3.png

由於《深入理解Android 卷一》和《深入理解Android卷二》不再出版,而知識的傳播不應該因為紙質媒介的問題而中斷,所以我將在OSC部落格中全文轉發這兩本書的全部內容

第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 概述

以本章做為本書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作為客戶端的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]

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;

//初始化Java Parcel類和Native層的關係

if(int_register_android_os_Parcel(env) < 0)

return -1;

return0;

}

據上面的程式碼可知,register_android_os_Binder函式完成了Java Binder架構中最重要的4個類的初始化工作。我們重點關注前3個。

1. Binder類的初始化

int_register_android_os_Binder函式完成了Binder類的初始化工作,程式碼如下:

[-->android_util_Binder.cpp]

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不是很清楚,可參閱卷I第2章“深入理解JNI”。

2. BinderInternal類的初始化

下一個初始化的類是BinderInternal,其程式碼在int_register_android_os_BinderInternal函式中。

[-->android_util_Binder.cpp]

static intint_register_android_os_BinderInternal(JNIEnv* env)

{

jclass clazz;

//根據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函式的實現

return AndroidRuntime::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]

static intint_register_android_os_BinderProxy(JNIEnv* env)

{

jclassclazz;

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

//gWeakReferenceOffsets用來和WeakReference類打交道

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

//獲取WeakReference類get函式的MethodID

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

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

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

//gErrorOffsets用來和Error類打交道

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

clazz =env->FindClass(kBinderProxyPathName);

//gBinderProxyOffsets用來和BinderProxy類打交道

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

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

...... //獲取BinderProxy的一些資訊

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

//gClassOffsets用來和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。

這幾個物件的命名中都有一個Offsets,我覺得這非常彆扭,不知道讀者是否有同感。

框架的初始化其實就是提前獲取一些JNI層的使用資訊,如類成員函式的MethodID,類成員變數的fieldID等。這項工作是必需的,因為它能節省每次使用時獲取這些資訊的時間。當Binder呼叫頻繁時,這些時間累積起來還是不容小覷的。

下面我們通過一個例子來分析Java Binder的工作流程。

2.2.3 窺一斑,可見全豹乎

這個例子源自ActivityManagerService,我們試圖通過它揭示Java層Binder的工作原理。先來描述一下該例子的分析步驟:

·首先分析AMS如何將自己註冊到ServiceManager。

·然後分析AMS如何響應客戶端的Binder呼叫請求。

本例的起點是setSystemProcess,其程式碼如下所示:

[-->ActivityManagerService.java]

public static void setSystemProcess() {

try {

ActivityManagerService m = mSelf;

//將ActivityManagerService服務註冊到ServiceManager中

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

}

......

return;

}

上面所示程式碼行的目的是將ActivityManagerService服務加到ServiceManager中。ActivityManagerService(以後簡稱AMS)是Android核心服務中的核心,我們以後會經常和它打交道。

大家知道,整個Android系統中有一個Native的ServiceManager(以後簡稱SM)程序,它統籌管理Android系統上的所有Service。成為一個Service的首要條件是先在SM中註冊。下面來看Java層的Service是如何向SM註冊的。

1. 向ServiceManager註冊服務

(1) 建立ServiceManagerProxy

向SM註冊服務的函式叫addService,其程式碼如下:

[-->ServiceManager.java]

public static void addService(String name, IBinderservice) {

try {

//getIServiceManager返回什麼

getIServiceManager().addService(name,service);

}

......

}

//分析getIServiceManager函式

private static IServiceManagergetIServiceManager() {

......

//呼叫asInterface,傳遞的引數型別為IBinder

sServiceManager= ServiceManagerNative.asInterface(

BinderInternal.getContextObject());

returnsServiceManager;

}

asInterface的引數為BinderInternal.getContextObject的返回值。這是一個native的函式,其實現的程式碼為:

[-->android_util_Binder.cpp]

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

}

[-->android_util_Binder.cpp]

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

*/

jobject object =(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) {

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。

大家還記得在Native層Binder中那個著名的interface_cast巨集嗎?在Java層中,雖然沒有這樣的巨集,但是定義了一個類似的函式asInterface。下面來分析ServiceManagerNative類的asInterface函式,其程式碼如下:

[-->ServiceManagerNative.java]

static public IServiceManager asInterface(IBinderobj)

{

...... //以obj為引數,建立一個ServiceManagerProxy物件

return new ServiceManagerProxy(obj);

}

上面程式碼和Native層interface_cast非常類似,都是以一個BpProxy物件為引數構造一個和業務相關的Proxy物件,例如這裡的ServiceManagerProxy物件。ServiceManagerProxy物件的各個業務函式會將相應請求打包後交給BpProxy物件,最終由BpProxy物件傳送給Binder驅動以完成一次通訊。

提示實際上BpProxy也不會和Binder驅動互動,真正和Binder驅動互動的是IPCThreadState。

(2) addService函式分析

現在來分析ServiceManagerProxy的addService函式,其程式碼如下:

[-->ServcieManagerNative.java]

public void addService(String name, IBinderservice)

throws RemoteException {

Parcel data = Parcel.obtain();

Parcel reply = 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]

static jbooleanandroid_os_BinderProxy_transact(JNIEnv* env, jobject obj,

jintcode, 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進行通訊的。

關於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]

public abstract class ActivityManagerNative

extends Binder

implementsIActivityManager

ActivityManagerNative從Binder派生,並實現了IActivityManager介面。下面來看ActivityManagerNative的建構函式:

[-->ActivityManagerNative.java]

public ActivityManagerNative() {

attachInterface(this, descriptor);//該函式很簡單,讀者可自行分析

}

//這是ActivityManagerNative父類的建構函式,即Binder的建構函式

public Binder() {

init();

}

Binder建構函式中會呼叫native的init函式,其實現的程式碼如下:

[-->android_util_Binder.cpp]

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]

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]

static void android_os_Parcel_writeStrongBinder(JNIEnv*env,

jobjectclazz, jobject object)

{

//parcel是一個Native的物件,writeStrongBinder的真正引數是

//ibinderForJavaObject的返回值

conststatus_t err = parcel->writeStrongBinder(

ibinderForJavaObject(env,object));

}

[-->android_util_Binder.cpp]

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驅動。

讀者此時容易想到,Java層中所有的Binder對應的都是這個JavaBBinder。當然,不同的Binder物件對應不同的JavaBBinder物件。

圖2-2展示了Java Binder、JavaBBinderHolder和JavaBBinder的關係。

圖2-2 JavaBinder、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第六章《深入理解Binder》。

[-->android_util_Binder.cpp]

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函式

jboolean res = env->CallBooleanMethod(mObject,

gBinderOffsets.mExecTransact,code,

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

......

return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;

}

就本例而言,上面程式碼中的mObject就是ActivityManagerService,現在呼叫它的execTransact函式,該函式在Binder類中實現,具體程式碼如下:

[-->Binder.java]

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

Parcel data = Parcel.obtain(dataObj);

Parcel reply = Parcel.obtain(replyObj);

boolean res;

try{

//呼叫onTransact函式,派生類可以重新實現這個函式,以完成業務功能

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

}......

reply.recycle();

data.recycle();

return res;

}

}

ActivityManagerNative類實現了onTransact函式,程式碼如下:

[-->ActivityManagerNative.java]

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;

}

由此可以看出,JavaBBinder僅是一個傳聲筒,它本身不實現任何業務函式,其工作是:

·當它收到請求時,只是簡單地呼叫它所繫結的Java層Binder物件的exeTransact。

·該Binder物件的exeTransact呼叫其子類實現的onTransact函式。

·子類的onTransact函式將業務又派發給其子類來完成。請讀者務必注意其中的多層繼承關係。

通過這種方式,來自客戶端的請求就能傳遞到正確的Java Binder物件了。圖2-3展示AMS響應請求的整個流程。

圖2-3 AMS響應請求的流程

圖2-3中,右上角的大方框表示AMS這個物件,其間的虛線箭頭表示呼叫子類過載的函式。

2.2.4 Java層Binder架構總結


圖2-4展示了Java層的Binder架構。

圖 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() {

nativeInit(); //建構函式呼叫nativeInit,該函式由Native層實現

}

nativeInit函式的真正實現為android_os_MessageQueue_nativeInit,其程式碼如下:

[-->android_os_MessageQueue.cpp]

static voidandroid_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {

//NativeMessageQueue是MessageQueue在Native層的代表

NativeMessageQueue*nativeMessageQueue = new NativeMessageQueue();

......

//將這個NativeMessageQueue物件設定到Java層儲存

android_os_MessageQueue_setNativeMessageQueue(env,obj,

nativeMessageQueue);

}

nativeInit函式在Native層建立了一個與MessageQueue對應的NativeMessageQueue物件,其建構函式如下:

[-->android_os_MessageQueue.cpp]

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]

final Message next() {

intpendingIdleHandlerCount = -1;

intnextPollTimeoutMillis = 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很簡單,不就是從以前在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]

final boolean enqueueMessage(Message msg, longwhen) {

......

final boolean 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);

}

return true;

}

上面的程式碼比較簡單,主要功能是:

·將message按執行時間排序,並加入訊息隊。

·根據情況呼叫nativeWake函式,以觸發nativePollOnce函式,結束等待。

建議雖然程式碼簡單,但是對於那些不熟悉多執行緒的讀者,還是要細細品味一下mBlocked值的作用。我們常說細節體現美,程式碼也一樣,這個小小的mBlocked正是如此。

2. nativeWake函式分析

nativeWake函式的程式碼如下所示:

[-->android_os_MessageQueue.cpp]

static voidandroid_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj,

jint ptr)

{

NativeMessageQueue*nativeMessageQueue = //取出NativeMessageQueue物件

reinterpret_cast<NativeMessageQueue*>(ptr);

returnnativeMessageQueue->wake(); //呼叫它的wake函式

}
void NativeMessageQueue::wake() {

mLooper->wake();//層層呼叫,現在轉到mLooper的wake函式

}

Native Looper的wake函式程式碼如下:

[-->Looper.cpp]

void Looper::wake() {

ssize_tnWrite;

do {

//向管道的寫端寫入一個字元

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

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

if(nWrite != 1) {

if(errno != EAGAIN) {

LOGW("Could not write wake signal, errno=%d", errno);

}

}

}

wake函式則更為簡單,僅僅向管道的寫端寫入一個字元”W”,這樣管道的讀端就會因為有資料可讀而從等待狀態中醒來。

2.3.3 nativePollOnce函式分析

nativePollOnce的實現函式是android_os_MessageQueue_nativePollOnce,程式碼如下:

[-->android_os_MessageQueue.cpp]

static void android_os_MessageQueue_nativePollOnce(JNIEnv*env, jobject obj,

jintptr, jint timeoutMillis)

NativeMessageQueue*nativeMessageQueue =

reinterpret_cast<NativeMessageQueue*>(ptr);

//取出NativeMessageQueue物件,並呼叫它的pollOnce

nativeMessageQueue->pollOnce(timeoutMillis);

}

//分析pollOnce函式

void NativeMessageQueue::pollOnce(inttimeoutMillis) {

mLooper->pollOnce(timeoutMillis); //重任傳遞到Looper的pollOnce函式

}

Looper的pollOnce函式如下:

[-->Looper.cpp]

inline int pollOnce(int timeoutMillis) {

return pollOnce(timeoutMillis, NULL, NULL, NULL);

}

上面的函式將呼叫另外一個有4個引數的pollOnce函式,這個函式的原型如下:

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

其中:

·timeOutMillis引數為超時等待時間。如果為-1,則表示無限等待,直到有事件發生為止。如果值為0,則無需等待立即返回。

·outFd用來儲存發生事件的那個檔案描述符

·outEvents用來儲存在該檔案描述符[①]上發生了哪些事件,目前支援可讀、可寫、錯誤和中斷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前,需要先通過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控制代碼中。

*/

structepoll_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_eventresultEvents[10];

inttimeout = -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發生了錯誤

}

elseif(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呢?

關於這個問題的答案,可參見筆者一篇博文“隨筆之如何實現一個執行緒池”。[②]

2. pollOnce函式分析

下面分析帶4個引數的pollOnce函式,程式碼如下:

[-->Looper.cpp]

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]

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

//從這裡也可看出NativeMessage和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函式返回,這時候可能有三種情況:

m 發生錯誤,則跳轉到Done處。

m 超時,這時候也跳轉到Done處。

m epoll_wait監測到某些檔案控制代碼上有事件發生。

·假設epoll_wait因為檔案控制代碼有事件而返回,此時需要根據檔案控制代碼來分別處理:

m 如果是管道讀這一端有事情,則認為是控制命令,可以直接讀取管道中的資料。

m 如果是其他FD發生事件,則根據Request構造Response,並push到Response陣列中。

·真正開始處理事件是在有Done標誌的位置。

m 首先處理Native的Message。呼叫Native Handler的handleMessage處理該Message。

m 處理Response陣列中那些帶有callback的事件。

上面的處理流程還是比較清晰的,但還是有個一個攔路虎,那就是mRequests,下面就來清剿這個攔路虎。

3. 新增監控請求

新增監控請求其實就是呼叫epoll_ctl增加檔案控制代碼。下面通過從Native的Activity找到的一個例子來分析mRequests。

[-->android_app_NativeActivity.cpp]

static jint

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

jstring funcName,jobject messageQueue,

jstring internalDataDir, jstring obbDir,

jstring externalDataDir, int sdkVersion,

jobject jAssetMgr, jbyteArraysavedState)

{

......

/*

呼叫Looper的addFd函式。第一個引數表示監聽的fd;第二個引數0表示ident;

第三個引數表示需要監聽的事件,這裡為只監聽可讀事件;第四個引數為回撥函式,當該fd發生

指定事件時,looper將回調該函式;第五個引數code為回撥函式的引數

*/

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

ALOOPER_EVENT_INPUT,mainWorkCallback, code);

......

}

Looper的addFd程式碼如下所示:

[-->Looper.cpp]

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

ALooper_callbackFunccallback, void* data) {

if (!callback) {

//判斷該Looper是否支援不帶回調函式的檔案控制代碼新增。一般不支援,因為沒有回撥函式

//Looper也不知道如何處理該檔案控制代碼上發生的事情

if(! mAllowNonCallbacks) {

return -1;

}

......

}

#ifdefLOOPER_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]

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]

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

constMessage& message) {

//Native的sendMessage函式必須同時傳遞一個Handler

nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);

sendMessageAtTime(now, handler, message); //呼叫sendMessageAtTime

}

void Looper::sendMessageAtTime(nsecs_t uptime,

const sp<MessageHandler>& handler,

const Message& message) {

size_t i= 0;

{ //acquire lock

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。

對Java程式設計師來說,以前單純的MessageQueue開始變得複雜。有同事經常與筆者討論,cpu並不是很忙,為什麼sendMessage的訊息很久後才執行?是的,對於只瞭解MessageQueue Java層的工作人員,這個問題還是沒辦法回答。因為MessageQueue在Native層的兄弟NativeMessageQueue可能正在處理一個Native的Message,而Java的呼叫堆疊資訊又不能列印Native層的活動,所以這個對Java程式設計師來說看上去很面善的MessageQueue,還是讓筆者為某些只沉迷於Java語言的同仁們感到擔心。

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派發以及處理來自所監控的檔案控制代碼的事件。



①注意,以後檔案描述符也會簡寫為檔案控制代碼。

[②]我的部落格地址是http://blog.csdn.net/innost

[③]我們這裡略過了Android2.2到Android 4.0之間幾個版本中的程式碼變化。

轉載於:https://my.oschina.net/innost/blog/487027