By 高煥堂 2009.7.31
台灣Android技術服務中心 主持人
“Android軟硬整合高階技術”課程 主講人
*** 其他相關文章
Java層的Activity透過BinderProxy來與遠距的(Remote)服務進行溝通。例如myBinder繼承Binder:
從Java層而觀之,myActivity可以經由bindService()而建立它與myBinder之間的連結。然而,這個連結是透過C++層的機制而達成的。如果你只想要開發Java層級的應用程式的話,並不需要閱讀本文。如果你想學習Android框架的原理,或修改Android框架,或撰寫Android的核心服務,進行軟硬整合,就必須熟悉幕後的重要機制。如下圖所示:
上圖之目的:myActivity想呼叫BinderProxy的IBinder::transact()函數,進而呼叫到myBinder的IBinder::onTransact()函數。就像一條橋,兩端分別是myActivity和myBinder。
為了要達成上述的目的,需要建立一些物件(如同橋墩),例如BpBinder物件,以及JavaBBinder物件;如此才能達成目的。茲詳細說明如下:
** 當myService::onCreate()函數誕生myBinder物件時,就呼叫到Binder()函數,它將myBinder物件的mObject欄位ID存入Native公用變數gBinderOffsets.mObject裡。接著,init()將JavaBBinderHolder物件的參考存入myBinder物件的mObject欄位裡。** 此時,若已知myBinder物件,藉由ibinderForJavaObject()可取得該物件相對應的JavaBBinderHolder物件。
** 例如,當myActivitty呼叫bindService()函數,就藉由ibinderForJavaObject()可取得該物件相對應的JavaBBinderHolder物件,然後從JavaBBinderHolder物件取得關於myBinder的資訊,進而誕生JavaBBinder物件來與myBinder物件相互輝映。當已知JavaBBinder物件,可藉由javaObjectForIBinder 可取得該物件相對應的myBinder物件。
** 建立了右邊的橋墩(即JavaBBinder物件)之後,也必須建立左邊的橋墩。Android的Binder System可以幫忙建立左邊的橋墩:BpBinder物件。
** 已知BpBinder物件,藉由javaObjectForIBinder ()可取得該物件相對應的BinderProxy物件, 或誕生新的BinderProxy物件。並且,將BindProxy物件的mObject欄位ID存入Native公用變數gBinderProxyOffsets.mObject裡。還將BpBinder物件的參考存入BinderProxy物件的mObject欄位裡。
** 有了BinderProxy物件,myActivity裡就能呼叫BinderProxy的IBinder::transact()函數,進而呼叫到myBinder的IBinder::onTransact()函數。也可藉由ibinderForJavaObject()可取得該物件相對應的BpBinder物件。
**** 以下是參考資料 ****
**** 是從Android框架原始程式碼裡面摘錄出來的,
提供給您參考。
---- BinderProxy類別是定義於Android 框架的/java/Binder.java檔案裡
final class BinderProxy implements IBinder {
public native boolean pingBinder();
public native boolean isBinderAlive();
public IInterface queryLocalInterface(String descriptor) {
return null;
}
public native String getInterfaceDescriptor() throws RemoteException;
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
public native void linkToDeath(DeathRecipient recipient, int flags)
throws RemoteException;
public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
BinderProxy() {
mSelf = new WeakReference(this);
}
@Override
protected void finalize() throws Throwable {
try {
destroy();
} finally {
super.finalize();
}
}
private native final void destroy();
private static final void sendDeathNotice(DeathRecipient recipient) {
if (Config.LOGV) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
try {
recipient.binderDied();
}
catch (RuntimeException exc) {
Log.w("BinderNative", "Uncaught exception from death notification",
exc);
}
}
final private WeakReference mSelf;
private int mObject;
}
其中,最重要的Native函數是:
public native boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
此Native函數實作於:
---- Native函數定義於Android 框架的/jni/android_util_binder.cpp檔案裡
static struct binderproxy_offsets_t
{
// Class state.
jclass mClass;
jmethodID mConstructor;
jmethodID mSendDeathNotice;
// Object state.
jfieldID mObject;
jfieldID mSelf;
} gBinderProxyOffsets;
在這gBinderProxyOffsets.mObject裡,放的是BpBinder。
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
{
if (dataObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return JNI_FALSE;
}
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
IBinder* target = (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
LOGV("Java code calling transact on %p in Java object %p with code %d/n",
target, obj, code);
//printf("Transact from Java code to %p sending: ", target); data->print();
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err);
return JNI_FALSE;
}
還含有:
sp
{
if (obj == NULL) return NULL;
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetIntField(obj, gBinderOffsets.mObject);
return jbh != NULL ? jbh->get(env) : NULL;
}
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
}
LOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
還含有:
static void android_os_Binder_init(JNIEnv* env, jobject clazz)
{
JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz);
if (jbh == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return;
}
LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
jbh->incStrong(clazz);
env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh);
}
static int int_register_android_os_Binder(JNIEnv* env)
{
jclass clazz;
clazz = env->FindClass(kBinderPathName);
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Binder");
gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gBinderOffsets.mExecTransact
= env->GetMethodID(clazz, "execTransact", "(IIII)Z");
assert(gBinderOffsets.mExecTransact);
gBinderOffsets.mObject
= env->GetFieldID(clazz, "mObject", "I");
assert(gBinderOffsets.mObject);
return AndroidRuntime::registerNativeMethods(
env, kBinderPathName,
gBinderMethods, NELEM(gBinderMethods));
}
還含有:
jobject javaObjectForIBinder(JNIEnv* env, const sp
{
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) {
// One of our own!
jobject object = static_cast
//printf("objectForBinder %p: it's our own %p!/n", val.get(), object);
return object;
}
// For the rest of the function we will hold this lock, to serialize
// looking/creation of Java proxies for native Binder proxies.
AutoMutex _l(mProxyLock);
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet);
if (res != NULL) {
LOGV("objectForBinder %p: found existing %p!/n", val.get(), res);
return res;
}
LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
LOGV("objectForBinder %p: created new %p!/n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
val->incStrong(object);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}