Android 四大組件 - bindService 的通信過程

相關(guān)文章鏈接:

1. Android Framework - 學(xué)習(xí)啟動篇
2. Android Binder 驅(qū)動 - Media 服務(wù)的添加過程
3. Android Binder 驅(qū)動 - 啟動 ServiceManager 進(jìn)程
4. Android Binder 驅(qū)動 - 內(nèi)核驅(qū)動層源碼分析
5. Android Binder 驅(qū)動 - 從驅(qū)動層來分析服務(wù)的添加過程
6. Android Binder 驅(qū)動 - 從 Java 層來跟蹤服務(wù)的查找過程
7. Android 四大組件 - 進(jìn)程的 fork 創(chuàng)建過程

相關(guān)源碼文件:

/frameworks/base/core/java/android/app/ContextImpl.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java

/frameworks/base/core/jni/android_util_Binder.cpp
/frameworks/base/core/jni/android_os_Parcel.cpp
/frameworks/native/libs/binder/Parcel.cpp
/drivers/staging/android/binder.c

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
/frameworks/base/core/java/android/app/ActivityThread.java

1. 代碼示例

本文主要從 bindService 的跨進(jìn)程通信與數(shù)據(jù)交互來入手分析雅宾,關(guān)于其具體的創(chuàng)建和綁定流程坷备,大家可以看看之前的內(nèi)容秽晚。aidl 一直都是一個晦澀難懂的內(nèi)容,筆者在幾年前也曾反復(fù)嘗試去了解其原理,其過程至今想想仍然是心有余悸趁桃,因此建議大家如果是剛?cè)胧?Android 其垄,只需要了解開發(fā)套路就可以了。但還是倡導(dǎo)大家笋颤,等有了一定技術(shù)沉淀之后,要嘗試著去理解其內(nèi)部實(shí)現(xiàn)原理内地。我們先來看一個簡單的示例代碼

// 客戶端
public class MainActivity extends AppCompatActivity {
    // 客戶端一定要獲取aidl的實(shí)例
    private UserAidl mUserAidl;
    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 鏈接好了  service就是服務(wù)端給我們的 IBinder
            mUserAidl = UserAidl.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 斷開鏈接
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,MessageService.class);
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    public void testClick(View view) {
        try {
            Log.e("TAG","userName = "+mUserAidl.getUserName());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

// 服務(wù)端
public class MessageService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        // 綁定
        return mBinder;
    }
    
    private final UserAidl.Stub mBinder = new UserAidl.Stub()
    {

        @Override
        public String getUserName() throws RemoteException {
            return "Darren@163.com";
        }

        @Override
        public String getUserPassword() throws RemoteException {
            return "19940223";
        }
    };
}

2. Binder 對象初始化

服務(wù)端返回的是 Aidl.Stub 服務(wù)對象伴澄,這個對象是我們自定義 aidl 后,編譯器幫我們自動生成的一個對象阱缓,該對象繼承自 Binder.java 對象非凌,子類在構(gòu)建對象時默認(rèn)會執(zhí)行父類的構(gòu)造函數(shù),也就是說會執(zhí)行 Binder.java 的構(gòu)造函數(shù):

    public Binder() {
        // 調(diào)用初始化方法
        init();
    }

    // 調(diào)用 native 方法
    private native final void init();

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

由上面的源碼可以看出荆针,Stub 對象在構(gòu)建的時候敞嗡,會對應(yīng)綁定 native 層的一個 JavaBBinderHolder 對象颁糟,至于為什么要創(chuàng)建 native 層對象,是因?yàn)榭邕M(jìn)程通信的核心是要基于 binder 驅(qū)動喉悴。

3. Stub 對象傳遞

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, Process.myUserHandle());
    }

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        IServiceConnection sd;
        ...
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                    mMainThread.getHandler(), flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess();
            // 像 AMS 發(fā)起 bind 請求
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }

由于 ServiceConnection 是一個普通對象棱貌,并不能用來做跨進(jìn)程傳遞,所以在 bindService 的源碼底層箕肃,系統(tǒng)幫我包裝了一個可以跨進(jìn)程傳遞的 IServiceConnection 對象婚脱,該對象是一個 Stub 對象。

    public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType, IServiceConnection connection,
            int flags,  String callingPackage, int userId) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeStrongBinder(token);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        // asBinder 返回的是 connection 自身
        data.writeStrongBinder(connection.asBinder());
        data.writeInt(flags);
        data.writeString(callingPackage);
        data.writeInt(userId);
        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        data.recycle();
        reply.recycle();
        return res;
    }

    static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
    {
      Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
      if (parcel != NULL) {
          const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
          if (err != NO_ERROR) {
              signalExceptionForError(env, clazz, err);
          }
      }
    }

    sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
    {
      if (obj == NULL) return NULL;
      // 返回的其實(shí)是一個 JavaBBinder
      if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
          JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
          return jbh != NULL ? jbh->get(env, obj) : NULL;
      }
      ...
      return NULL;
    }
    // 這個之前的文章有講就不再貼代碼了
    status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
    {
      return flatten_binder(ProcessState::self(), val, this);
    }

上面會調(diào)用 writeStrongBinder 方法將 IServiceConnection 對象寫入到 Parcel 內(nèi)存中勺像,其實(shí)也就是將 native 層的 JavaBBinder 對象寫入到 native 層的 Parcel 中障贸,關(guān)于 flatten_binder 方法的實(shí)現(xiàn)原理大家可以參考之前的文章。

4. 客戶端驅(qū)動層處理過程

我們向 ServiceManager 查詢到的 AMS 對象吟宦,其實(shí)是一個 BindProxy.java 對象篮洁,對應(yīng) native 層的 BpBinder 對象,也就是說 mRemote.transact 最后會調(diào)用 BpBinder.cpp 的 transact 方法督函。

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    if (reply) {
        ...
    } else {
        if (tr->target.handle) {
            struct binder_ref *ref;
            // 找到 AMS 目標(biāo)進(jìn)程的 binder_node 和 binder_proc
            ref = binder_get_ref(proc, tr->target.handle);
            target_node = ref->node;
        } else {
            ...
        }
                ...
    }   
    ...
    off_end = (void *)offp + tr->offsets_size;
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        ...
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        switch (fp->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            struct binder_ref *ref;
            // 先通過 fp->binder 從自己進(jìn)程找 binder_node 
            struct binder_node *node = binder_get_node(proc, fp->binder);
            // 剛開始第一次是找不到的
            if (node == NULL) {
                // 把他添加到當(dāng)前進(jìn)程
                node = binder_new_node(proc, fp->binder, fp->cookie);
                ...
            }
            // 添加到目標(biāo)進(jìn)程
            ref = binder_get_ref_for_node(target_proc, node);
            if (ref == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
            // 替換 type 和 handle 值 
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc;
        } break;
        ...
        }
    }
    
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
        // 喚醒目標(biāo)進(jìn)程的等待隊(duì)列
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;
}

5. AMS 服務(wù)端處理 bind 過程

AMS 接收到 bindService 請求后會去讀取 IServiceConnection 對象嘀粱,注意這里讀取到的 IServiceConnection 其實(shí)已經(jīng)是本地的一個 BpBinder 了,只不過我們可以通過 BpBinder 對象回調(diào)到客戶端的 onServiceConnected 綁定方法辰狡。

case BIND_SERVICE_TRANSACTION: {
  data.enforceInterface(IActivityManager.descriptor);
  IBinder b = data.readStrongBinder();
  IApplicationThread app = ApplicationThreadNative.asInterface(b);
  IBinder token = data.readStrongBinder();
  Intent service = Intent.CREATOR.createFromParcel(data);
  String resolvedType = data.readString();
  b = data.readStrongBinder();
  int fl = data.readInt();
  String callingPackage = data.readString();
  int userId = data.readInt();
  IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
  int res = bindService(app, token, service, resolvedType, conn, fl, callingPackage, userId);
  reply.writeNoException();
  reply.writeInt(res);
  return true;
}

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

6. 進(jìn)程循環(huán)等待處理請求

服務(wù)端處理客戶端的請求锋叨,肯定會有一個等待執(zhí)行處理的過程,但整個交互邏輯流程分析下來宛篇,并沒有發(fā)現(xiàn)服務(wù)端有等待的過程娃磺。那服務(wù)端到底是怎樣進(jìn)入等待的呢?這個得追溯到 Zygote 創(chuàng)建進(jìn)程的流程去叫倍。

    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();

        commonInit();
        nativeZygoteInit();
        applicationInit(targetSdkVersion, argv, classLoader);
    }

    private static final native void nativeZygoteInit();

    static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
    {
      gCurRuntime->onZygoteInit();
    }

    virtual void onZygoteInit()
    {
        // 初始化 binder 驅(qū)動
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        // 啟動一個線程進(jìn)入循環(huán)處理客戶端 binder 請求
        proc->startThreadPool();
    }
    
    void ProcessState::spawnPooledThread(bool isMain)
    {
      if (mThreadPoolStarted) {
          String8 name = makeBinderThreadName();
          ALOGV("Spawning new pooled thread, name=%s\n", name.string());
          sp<Thread> t = new PoolThread(isMain);
          t->run(name.string());
      }
    }

    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)
    {
      ...
      status_t result;
      do {
          ...
          // 進(jìn)入 binder 驅(qū)動的 wait 隊(duì)列上進(jìn)行等待
          result = getAndExecuteCommand();
          ...
      } while (result != -ECONNREFUSED && result != -EBADF);
      ...
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偷卧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吆倦,更是在濱河造成了極大的恐慌听诸,老刑警劉巖蚕泽,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荒吏,死亡現(xiàn)場離奇詭異锡宋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門摊崭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矮台,“玉大人蛤迎,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵峰搪,是天一觀的道長概耻。 經(jīng)常有香客問我使套,道長,這世上最難降的妖魔是什么鞠柄? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任侦高,我火速辦了婚禮,結(jié)果婚禮上厌杜,老公的妹妹穿的比我還像新娘奉呛。我一直安慰自己,他們只是感情好夯尽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布瞧壮。 她就那樣靜靜地躺著,像睡著了一般匙握。 火紅的嫁衣襯著肌膚如雪咆槽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天圈纺,我揣著相機(jī)與錄音秦忿,去河邊找鬼。 笑死蛾娶,一個胖子當(dāng)著我的面吹牛灯谣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茫叭,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酬屉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了揍愁?” 一聲冷哼從身側(cè)響起呐萨,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莽囤,沒想到半個月后谬擦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朽缎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年惨远,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片话肖。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡北秽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出最筒,到底是詐尸還是另有隱情贺氓,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布床蜘,位于F島的核電站辙培,受9級特大地震影響蔑水,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扬蕊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一搀别、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尾抑,春花似錦歇父、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至践磅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灸异,已是汗流浹背府适。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肺樟,地道東北人檐春。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像么伯,于是被迫代替她去往敵國和親疟暖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容