Android 平臺 Binder 機制 簡介

Binder : android 平臺的一種IPC機制,在kernel中封裝消息規則,在用戶空間提供接口對消息規則進一步封裝,以達到IPC通信的目的。
Android 平臺中的基本組件,Activity,Service,BroadcastReceiver,ContentProvider,都是基於Binder進行IPC通訊。

下面以ContentProvider爲例進行說明Binder的通信機制。

Androdid平臺一個App可以訪問另外一個App中的數據庫:


這裏寫圖片描述

  • AMS: ActivityManagerService
  • App2: 實現數據庫服務的應用
  • App1: 訪問數據庫的應用
  • Binder驅動:Binder驅動層支持
  • IContentProvider:數據庫服務跨進程訪問接口

數據庫跨進程訪問基本流程:

  1. App2啓動之後,會將數據庫的IContentProvider接口publish到AMS,即BpBinder
  2. App1從AMS中得到數據庫的IContentProvider接口,將其封裝,保存在本地,即BpBinder
  3. 得到接口之後,就可以通過IContentProvider接口訪問App2中的數據,,不必經過AMS

Step 1:App2啓動並將數據庫publish到AMS

  • 應用啓動,install provider:
private void handleBindApplication(AppBindData data) {
    if (!data.restrictedBackupMode) {
                List<ProviderInfo> providers = data.providers;
                if (providers != null) {
                    installContentProviders(app, providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }
}

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
        }
    }
  • 將ContentProvider publish到 AMS
    public void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeTypedList(providers);
        mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

Step 2:App1從AMS中得到數據庫的IContentProvider接口

  • 獲取數據庫接口一般通過如下語句:
ContentResolver resolver = context.getContentResolver();

resolver.query(...);
  • ContentResolver 的實現位於ContextImpl.java ApplicationContentResolver
private static final class ApplicationContentResolver extends ContentResolver {
        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
}

  • query 流程中會通過 acquiryProvider 得到 IContentProvider 接口:
    public ContentProviderHolder getContentProvider(IApplicationThread caller,
            String name, int userId, boolean stable) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(name);
        data.writeInt(userId);
        data.writeInt(stable ? 1 : 0);
        mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        ContentProviderHolder cph = null;
        if (res != 0) {
            cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
        }
        data.recycle();
        reply.recycle();
        return cph;
    }

Step 3:得到接口之後,通過IContentProvider接口訪問App2中的數據庫數據

上面大致說了一下IPC通信的基本流程,具體通過Binder傳遞Binder對象下面進一步說明。

1、服務端Binder對象
服務端的Binder對象,這裏是ContentProvider都是繼承自Binder,對java層來說。

class Transport extends ContentProviderNative
abstract public class ContentProviderNative extends Binder implements IContentProvider

可以看到這個ContentProvider中的Transport就是一個Binder。
既然是一個Binder,需要了解一下這個Binder對象的初始化過程:

public class Binder implements IBinder{
    public Binder() {
        init();//這是一個native方法
    }
    private native final void init();
}

native實現
static void android_os_Binder_init(JNIEnv* env, jobject obj)
{
    JavaBBinderHolder* jbh = new JavaBBinderHolder();
    jbh->incStrong((void*)android_os_Binder_init);
    env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);
}

這裏的 JavaBBinderHolder 在進行進程間通信的時候或調用get方法,得到一個BBinder:

    sp<JavaBBinder> get(JNIEnv* env, jobject obj)
    {
        AutoMutex _l(mLock);
        sp<JavaBBinder> b = mBinder.promote();
        if (b == NULL) {
            b = new JavaBBinder(env, obj);
            mBinder = b;
            ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
                 b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
        }

        return b;
    }

class JavaBBinder : public BBinder

也就是說,我們說的java層的Binder都有一個Native對應的BBinder。
Binder作爲服務端,除非died,否則應該一直等待client發來消息,那麼這個Binder是如何實現等待呢?
這就涉及到App啓動過程中線程池的開啓,App都是zygote進行fork出來的,在初始化的時候會開啓線程池:

virtual void onZygoteInit()
{
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}
class PoolThread : public Thread
{
public:
    PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    }

protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }

    const bool mIsMain;
};

void IPCThreadState::joinThreadPool(bool isMain)
{
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
    do {
        result = getAndExecuteCommand();
    } while (result != -ECONNREFUSED && result != -EBADF);
}
status_t IPCThreadState::getAndExecuteCommand()
{
    result = talkWithDriver();
    result = executeCommand(cmd);
}

talkWithDriver會利用系統調用ioctl告訴kernel BC_ENTER_LOOPER,就是說我服務端準備好了。
當收到client的消息之後,就調用onTransact,也就是 JavaBBinder的onTransact

virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
}

private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) {

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

就調用到Binder的 onTransact,解析命令,執行對應的函數。
2、Client BpBinder 對象
待續。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章