編輯:關於Android編程
前兩天看到Service的onBind函數返回值的三種情況(擴展Binder對象,Messenger,aidl),一直在想他們內部的實現有什麼不一樣的,網上很多文章都介紹了Service的綁定過程,但是並沒有介紹對於跨進程與非跨進程,對於不同的返回值,其具體有什麼區別,以及具體是怎麼實現的。這篇文章就根據源碼分析Android究竟是在哪部分來控制跨進程與非跨進程Binder的傳輸的,Binder究竟是怎麼傳輸的。
首先看一下Service的綁定中,Binder跨進程與非跨進程的區別代碼中的體現。
想知道具體是怎麼樣的,其實直接在ServiceConnection的onServiceConnection回調函數中,以及在Service的onBind中分別打印一下Binder就好了,如下代碼
//Service onBind
public Binder onBind(){
Binder binder ; //這裡並沒有初始化,可以使擴展Binder,也可以是aidl的Stub
Log.i("TestLog","onBind: "+binder.hashCode()+","+binder.toString());
return binder;
}
//ServiceConnection
ServiceConnection connection = new ServiceConnection(){
public void onServiceConnection(ComponentName component, Binder binder){
Log.i("TestLog","onServiceConnection: "+binder.hashCode()+","+binder.toString());
}
}
通過設置Service的android:process,可以將Service遠程進程與相同進程進行實驗。
通過log可以發現,如果是跨進程的話,在ServiceConnection的onServiceConnection返回的結果是一個BinderProxy對象,與onBind中打印的結果是不一致的。如果是非跨進程,在onServiceConnection與onBind的打印的內容是一模一樣的。這裡跟onBind是返回擴展Binder還是返回擴展aidl自動產生Stub類是沒有關系的,只跟是否跨進程有關。
從這裡可以看出,如果是跨進程則返回BinderProxy,非跨進程則返回原來在onBind中返回的對象。我困惑的問題是,Android是怎麼實現這個根據不同的進程來返回不同的結果呢?下面先看一下Service的綁定過程是否有針對不同進程的區別處理。
首先簡單介紹一下Service的綁定過程:
1. Activity通過bindService請求綁定相應的Service(由Intent指定),並設置了ServiceConnection回調接口。
2. 實際上會調用ContextImpl.bindService,然後調用ActivityManagerNative.getDefault().bindService,也就是通過IPC調用ActivityManagerService的bindService函數。
3. 在ActivityManagerService的bindService函數中,先通過retrieve創建ServiceRecord,這個就是對應著我們想要綁定的Service
4. 然後根據結果創建ConnectionRecord,將ServiceConnection保存在這裡面。ConnectionRecord保存在一個ArrayList中,可能有多個客戶端想要綁定。
5. 接著在bringUpServiceLocked函數中判斷是否需要重新創建一個進程來運行Service,根據android:processd的設置
6. 啟動服務,通過ApplicationThreadProxy(app.thread)遠程調用ApplicationThread的scheduleCreateService來發送Message給主線程消息循環(ActivityThread,ActivityThread.H),啟動服務。
7. 綁定服務,通過ApplicationThreadProxy(app.thread)遠程調用ApplicationThread的scheduleBindService來發送Message給主線程消息循環(ActivityThread,ActivityThread.H),綁定服務(handleBindService)。這裡看一下源碼:
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
在綁定服務中,調用Service的onBind方法得到Binder,並且通過ActivityManagerNative.getDefault().publishService發布服務。 在publishServcie中使用ServiceRecord獲取ConnectionRecord,最終調用保存在ConnectionRecord裡面的ServiceConnection的onServiceConnection函數。其中ServiceRecord,ServiceConnection都是Binder類,可以通過IPC訪問。
這裡只是簡單介紹一下綁定Service的流程,也並沒有把具體對象名稱寫出來。如果想要更詳細的了解整個過程可以去看一下老羅的博客,其實最好是照著別人的博客看一下源碼。
我一直覺得是在綁定服務的過程中會針對不同的onBind返回值有不同的處理,但實際上並沒有。看第7步中的源碼就知道了,上面的過程中,並沒有針對不同的onBind返回值進行特殊處理。
既然綁定過程中沒有對不同進程進行處理,那麼只能看看Parcel了。而實際上確實是在Parcel中有體現出來了跨進程與非跨進程的區別。下面看看Parcel讀取和寫入Binder。
Parcel寫入Binder是通過writeStrongBinder的,Java層的writeStrongBinder是一個native函數,對應的JNI實現為:
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
其中ibinderForJavaObject是將java層的對象轉換成native層的Binder對象,實際上對應的是JavaBBinder。
然後native層的parcel會調用它的writeStrognBinder:
status_t Parcel::writeStrongBinder(const sp& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
最終調用flatten_binder:
status_t flatten_binder(const sp& /*proc*/,
const sp& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder(); //JavaBBinder返回的是this,也就是自己
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {// 寫入JavaBBinder將會對應這一段。
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast(local->getWeakRefs());
obj.cookie = reinterpret_cast(local); //把對應的JavaBBinder的指針轉換成整形uintptr_t
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return finish_flatten_binder(binder, obj, out); //finish_flatten_binder是將obj寫入到out裡面。
}
這裡就根據是本地binder還是遠程binder,對Binder的寫入采取了兩種不同的方式。Binder如果是JavaBBinder,則它的localBinder會返回localBinder,如果是BpBinder則localBinder會為null。我們寫入的時候,ibinderForJavaObject就返回的是JavaBBinder。flat_binder_object是Binder寫入的時候的對象,它對應著Binder。handle表示Binder對象在Binder驅動中的標志,比如ServiceManager的handle為0。type表示當前傳輸的Binder是本地的(同進程),還是一個proxy(跨進程)。binder,cookie保存著Binder對象的指針。finish_flatten_binder會將obj寫入到out裡面,最終寫入到Binder驅動中,寫入部分也就完了。從上面的流程可以看出,在寫入的時候,Parcel會針對不同的Binder(BBinder/JavaBBinder,BpBinder)有不同的處理,而他們確實就是對應著跟Service端Binder是同一個進程的還是不同進程的情況。
接下來就是讀取了,同樣的,Java層的Parcel也是通過native函數來讀取的。在這裡我們從最底層開始分析,首先從unflatten_binder開始:
unflatten_binder:
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
首先從Binder驅動中讀取一個flat_binder_object—flat。flat的處理是關鍵,它會根據flat->type的值分別處理,如果是BINDER_TYPE_BINDER,則使用cookie中的值強制轉換成指針。如果是BINDER_TYPE_HANDLE,則使用Proxy,getStringProxyForHandle會返回BpBinder。而調用unflatten_binder的是native層的Parcel的readStringBinder。
sp Parcel::readStrongBinder() const
{
sp val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
調用readStrongBinder的是jni函數的實現:
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
javaObjectForIBinder與ibinderForJavaObject相對應,把IBinder對象轉換成對應的Java層的Object。這個函數是關鍵。看看它的實現:
jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
{
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) { //如果是本地的,那麼會直接進入這部分代碼,因為這個val是寫入的時候同一個對象,gBinderOffsets也是一致。如果val是一種proxy對象,則不然,會繼續往下執行找到一個Proxy對象。
// One of our own!
jobject object = static_cast(val.get())->object();
LOGDEATH("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 = jniGetReferent(env, object);
if (res != NULL) {
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
}
LOGDEATH("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);//gBinderProxyOffsets保存的是Java層BinderProxy的信息,這裡也是創建BinderProxy。
if (object != NULL) {
LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
val->incStrong((void*)javaObjectForIBinder);
// 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);
// Also remember the death recipients registered on this proxy
sp drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
//IBinder
checkSubclass(const void* subclassID) const
{
return subclassID == &gBinderOffsets;
}
上面的處理中,如果是跟Service是同一個進程,也就是val是JavaBBinder。那麼在checkSubclass中,它所包含的gBinderOffsets指針與參數傳入的gBinderOffsets的指針必然是同一個值,則滿足if條件。直接將指針強制轉換成JavaBBinder,返回對應的jobject。如果是不同的進程,那麼val也就會是BpBinder,最終會返回一個BinderProxy,不同的進程這一部分網上很多介紹Binder的文章都介紹了。
上面介紹了Parcel整個寫入讀取的流程,最後代替Binder傳輸的是flat_binder_object。在native的Parcel中,根據跨進程還是非跨進程,flat_binder_object的值是不一樣的,跨進程的時候flat_binder_object的type為BINDER_TYPE_HANDLE,非跨進程的時候flat_binder_object的type為BINDER_TYPE_BINDER。在這裡已經可以發現跨進程與非跨進程的時候傳輸的數據的區別了。客戶端的Parcel在讀取Binder的時候,根據是flat_binder_object的type的值進行區分對待,返回不同的內容。
但是我們還沒有發現Android是如何區分跨進程與非跨進程的呢?顯然它的實現在Binder驅動裡面。關於如何判斷讀取的時候是跟Service同一個進程還是不同的進程,腦子裡隨便一想基本就能夠想到怎麼去實現了。因為每個進程進行系統調用陷入內核的時候,內核的當然是可以知道當前進入內核空間的進程的信息了啦,這樣就可以判斷當前請求讀取信息的是跟Service同一個進程還是不同的進程了。
實際上Binder驅動保存著Service端的Binder地址和handle的信息,將兩者相互映射,根據不同的服務端進程和客戶端進程來區別處理。
實際上真正控制跨進程與非跨進程返回Binder類型的,不是綁定服務的過程,而是Binder驅動。Parcel是根據從驅動中讀取的數據作出不同的處理,而Binder驅動真正控制。如果是與Service端同一個進程則返回Binder(在底層是Binder指針),如果是不同的進程則返回handle。真正在傳輸的過程中,是作為flat_binder_object對象來傳遞的。
其實也可以在Service的綁定過程中來根據是否是相同進程來進行處理的,但那樣就局限於Service的綁定部分了,而Android中用的Binder傳輸的實在是太多了,所以直接在Binder驅動中進行控制更加方便。
積跬步,至千裡;積小流,成江海
最近項目中需要完成以下這個需求UI給我了五張圖片,我感覺太浪費了,自定義view完全可以做而且適配起來更加的方便最終實現效果項目效果擴展需要知道技術點在實現這個過程之前,
Android的traces.txt文件記錄了進程的堆棧情況,對於分析應用響應慢,以及ANR的原因很有幫助。traces.txt文件的位置位於/data/anr/。1.
本文實例講述了Android實現將應用崩潰信息發送給開發者並重啟應用的方法。分享給大家供大家參考,具體如下:在開發過程中,雖然經過測試,但在發布後,在廣大用戶各種各樣的運
首先上效果圖實現原理要完成一個輪播圖片,首先想到的應該是使用ViewPager來實現。ViewPager已經有了滑動的功能,我們只要讓它自己滾動。再加上下方的小圓點就行了