[深入理解Android卷二 全文-第二章]深入理解Java Binder和MessageQueue
由於《深入理解Android 卷一》和《深入理解Android卷二》不再出版,而知識的傳播不應該因為紙質媒介的問題而中斷,所以我將在CSDN部落格中全文轉發這兩本書的全部內容
第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派發以及處理來自所監控的檔案控制程式碼的事件。
相關文章
- 深入理解Java中的鎖(二)Java
- 深入理解Java虛擬機器(二)Java虛擬機
- Java中Thread 和Runnable 深入理解Javathread
- 深入理解Java PriorityQueueJava
- 深入理解 Java 方法Java
- Java:IO:深入理解Java
- 深入理解Java反射Java反射
- 理解 Android Binder 機制(二):C++層AndroidC++
- SpringIOC二—— 容器 和 Bean的深入理解SpringBean
- 【Java】Java容器篇(二),深入理解List集合類Java
- Android 深入理解 Notification 機制Android
- 深入理解 Java 反射和動態代理Java反射
- 深入理解 Java 泛型Java泛型
- 深入理解 Java 註解Java
- Java集合——深入理解HashMapJavaHashMap
- 深入理解Java異常Java
- 深入理解Java反射(一)Java反射
- 深入理解Java泛型Java泛型
- 深入理解 Java 列舉Java
- 深入理解 Java 陣列Java陣列
- 深入理解Emoji(二) —— 位元組序和BOM
- 圖解 Android 系列(二)深入理解 init 與 zygote 程式圖解AndroidGo
- 非同步(二):Generator深入理解非同步
- Java核心(二)深入理解執行緒池ThreadPoolJava執行緒thread
- 深入理解Java記憶體模型(二)——重排序Java記憶體模型排序
- 深入學習和理解 ReduxRedux
- 深入理解Android訊息機制Android
- 深入理解Android逆向除錯原理Android除錯
- 深入理解Java中的AQSJavaAQS
- Java基礎——深入理解反射Java反射
- 深入理解Java I/O模型Java模型
- 深入理解 Java 序列化Java
- 從Java角度深入理解KotlinJavaKotlin
- 深入理解Java物件結構Java物件
- 深入理解Java中的鎖Java
- 深入理解 Java 中的 LambdaJava
- 深入理解Activity啟動流程和AMS框架(二)框架
- 深入理解多執行緒(二)—— Java的物件模型執行緒Java物件模型
- 深入理解hashmap(二)理論篇HashMap