Android面試題:bindService獲取代理是同步還是異步

Android中bindService是一個異步的過程,什么意思呢?使用bindService無非是想獲得一個Binder服務的Proxy,但這個代理獲取到的時機并非由bindService發起端控制,而是由Service端來控制,也就是說bindService之后,APP端并不會立刻獲得Proxy,而是要等待Service通知APP端,具體流程可簡化如下:

  • APP端先通過bindService去AMS登記,說明自己需要綁定這樣一個服務,并留下派送地址
  • APP回來,繼續做其他事情,可以看做是非阻塞的
  • AMS通知Service端啟動這個服務
  • Service啟動,并通知AMS啟動完畢
  • AMS跟住之前APP端留下的地址通知APP端,并將Proxy代理傳遞給APP端

通過代碼來看更直接

    void test(){
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
               iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
               Log.v(TAG, "onServiceConnected..." );
            }
           @Override
            public void onServiceDisconnected(ComponentName componentName) {
           }
        }, Context.BIND_AUTO_CREATE);
        Log.v(TAG, "end..." );
    }

bindService的過程中,上面代碼的Log應該是怎么樣的呢?如果bindService是一個同步過程,那么Log應該如下:

TAG  onServiceConnected ...
TAG  end ...

但是由于是個異步過程,真實的Log如下

TAG  end ...    
TAG  onServiceConnected ...

也就是說bindService不會阻塞等待APP端獲取Proxy,而是直接返回,這些都可以從源碼獲得支持,略過,直接去ActivityManagerNative去看

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType, IServiceConnection connection,
        int flags, 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);
    data.writeStrongBinder(connection.asBinder());
    data.writeInt(flags);
    data.writeInt(userId);
    <!--阻塞等待-->
    mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    int res = reply.readInt();
    data.recycle();
    reply.recycle();
    return res;
}

mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0)確實會讓APP端調用線程阻塞,等待AMS執行BIND_SERVICE_TRANSACTION請求,不過AMS在執行這個請求的時候并非是喚醒Service才返回,它返回的時機更早,接著看ActivityManagerService,

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType,
        IServiceConnection connection, int flags, int userId) {
    ...
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service, resolvedType,
                connection, flags, userId);
    }
}

ActivityManagerService直接調用ActiveServices的函數bindServiceLocked,請求綁定Service,到這里APP端線程依舊阻塞,等待AMS端返回,假定Service所處的進程已經啟動但是Service沒有啟動,這時ActiveServices會進一步調用bindServiceLocked->realStartServiceLocked來啟動Service,有趣的就在這里:

 private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
        ...
        <!--請求Service端啟動Service-->
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
        ...
        <!--請求綁定Service-->
        requestServiceBindingsLocked(r);

app.thread.scheduleCreateService也是一個Binder通信過程,他其實是AMS異步請求ActivityThread中的ApplicationThread服務,系統服務請求客戶端的本地服務一般都是異步的:

// 插入消息,等待主線程執行
public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo) {
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
    <!--向Loop的MessagerQueue插入一條消息就返回-->
    queueOrSendMessage(H.CREATE_SERVICE, s);
}

不過,這個請求直接向Service端的ActivityThread線程中直接插入一個消息就返回了,而并未等到該請求執行,因為AMS使用的非常頻繁,不可能老等待客戶端完成一些任務,所以AMS端向客戶端發送完命令就直接返回,這個時候其實Service還沒有被創建,也就是這個請求只是完成了一半,onServiceConnected也并不會執行,onServiceConnected什么時候執行呢?app.thread.scheduleCreateService向APP端插入第一條消息,是用來創建Service的, requestServiceBindingsLocked其實就是第二條消息,用來處理綁定的

 private final boolean requestServiceBindingLocked(ServiceRecord r,
            IntentBindRecord i, boolean rebind) {
                ...
           <!-- 第二個消息,請求處理綁定-->
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);

第二條消息是處理一些綁定需求,Android的Hanlder消息處理機制保證了第二條消息一定是在第一條消息之后執行,

 public final void scheduleBindService(IBinder token, Intent intent,
        boolean rebind) {
    BindServiceData s = new BindServiceData();
    s.token = token;
    s.intent = intent;
    s.rebind = rebind;
    queueOrSendMessage(H.BIND_SERVICE, s);
}      

以上兩條消息插入后,AMS端被喚醒,進而重新喚醒之前阻塞的bindService端,而這個時候,Service并不一定被創建,所以說這是個未知的異步過程,Service端處理第一條消息的時會創建Service,

 private void handleCreateService(CreateServiceData data) {
    ...
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
   ...

執行第二條消息的時候, 會向AMS請求publishService,其實就是告訴AMS,服務啟動完畢,可以向之前請求APP端派發代理了。

 private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
       try {
        data.intent.setExtrasClassLoader(s.getClassLoader());
        try {
            if (!data.rebind) {
                IBinder binder = s.onBind(data.intent);
                ActivityManagerNative.getDefault().publishService(
                        data.token, data.intent, binder);
            ...                       

AMS端收到publishService消息之后,才會向APP端發送通知,進而通過Binder回調APP端onServiceConnected函數,同時傳遞Proxy Binder服務代理

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    ...
     try {
    <!--通過binder 回到APP端的onServiceConnected--> 
        c.conn.connected(r.name, service);
    } catch (Exception e) {

到這里,onServiceConnected才會被回調,不過,至于Service端那兩條消息什么時候執行,誰也不能保證,也許因為特殊原因,那兩條消息永遠不被執行,那onServiceConnected也就不會被回調,但是這不會影響AMS與APP端處理其他問題,因為這些消息是否被執行已經不能阻塞他們兩個了,簡單流程如下:

bindService的異步流程

最后,其實startService也是異步。

作者:看書的小蝸牛
Android面試題:bindService獲取代理是同步還是異步

僅供參考,歡迎指正

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。