1. 程式人生 > >[Android] bindService的binder通訊過程分析

[Android] bindService的binder通訊過程分析

關於bindService方法

public class ContextWrapper extendsContext {

   Context mBase;

 

   public ContextWrapper(Context base) {

        mBase = base;

   }

 

   public boolean bindService(Intent service, ServiceConnection conn,

            int flags) {

       returnmBase.bindService(service, conn, flags);

   }

 

ContextImpl.java

   // bindService

   @Override

   public boolean bindService(Intent service, ServiceConnection conn, intflags) {

        warnIfCallingFromSystemProcess();

        return bindServiceCommon(service, conn,flags, Process.myUserHandle());

   }

 

   // bindServiceCommon

   private boolean bindServiceCommon(Intent service, ServiceConnectionconn, int flags, UserHandle user) {

        IServiceConnection sd;

        ...

//包裝ServiceConnection

        sd =mPackageInfo.getServiceDispatcher(conn, getOuterContext(),

                mMainThread.getHandler(),flags);

 

 

        ...

 

        int res = ActivityManagerNative.getDefault().bindService(

               mMainThread.getApplicationThread(), getActivityToken(), service,

               service.resolveTypeIfNeeded(getContentResolver()),

                sd, flags, getOpPackageName(),user.getIdentifier());

 

        ...

 

   }

 

這裡是通過binder和AMS進行通訊,一次binder呼叫。

 

在通訊的時候,構造了一個InnerConnection的binder物件作為引數傳給了AMS,以便於實現方法回撥。

        private static class InnerConnectionextends IServiceConnection.Stub {

            finalWeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

 

           InnerConnection(LoadedApk.ServiceDispatcher sd) {

                mDispatcher = newWeakReference<LoadedApk.ServiceDispatcher>(sd);

            }

 

            public void connected(ComponentNamename, IBinder service) throws RemoteException {

               LoadedApk.ServiceDispatcher sd = mDispatcher.get();

                if (sd != null) {

                    sd.connected(name,service);

                }

            }

        }

 

 

另外構造了ServiceDispatcher物件將ServiceConnection和InnerConnection進行了包裝。

 

 

在回撥的時候,AMS端作為Proxy呼叫connected方法。

 

在AMS中的呼叫比較複雜,時序圖如下

AMS的bindService方法

    public intbindService(IApplicationThread caller, IBinder token, Intent service,

            String resolvedType,IServiceConnection connection, int flags, String callingPackage,

            int userId) throwsTransactionTooLargeException {

       enforceNotIsolatedCaller("bindService");

 

        // Refuse possible leaked filedescriptors

        if (service != null &&service.hasFileDescriptors() == true) {

            throw newIllegalArgumentException("File descriptors passed in Intent");

        }

 

        if (callingPackage == null) {

            throw newIllegalArgumentException("callingPackage cannot be null");

        }

 

        synchronized(this) {

            return mServices.bindServiceLocked(caller, token,service,

                    resolvedType, connection,flags, callingPackage, userId);

        }

   }

裡面呼叫到mServices.bindServiceLocked

在ActivityManagerService.java中有定義

   final ActiveServices mServices;

 

繼續呼叫到其realStartServiceLocked方法

public final class ActiveServices {

   private final void realStartServiceLocked(ServiceRecord r,

            ProcessRecord app, booleanexecInFg) throws RemoteException {

 

     ...

 

        r.app = app;

 

       ...

 

            app.thread.scheduleCreateService(r,r.serviceInfo,

                   mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),

                    app.repProcState);

            r.postNotification();

 

    ...

    //

        requestServiceBindingsLocked(r, execInFg);

 

       updateServiceClientActivitiesLocked(app, null, true);

 

 

    ...

   }

 

 

 

private final boolean requestServiceBindingLocked(ServiceRecordr, IntentBindRecord i,

            boolean execInFg, boolean rebind)throws TransactionTooLargeException {

        if (r.app == null || r.app.thread ==null) {

            // If service is not currentlyrunning, can't yet bind.

            return false;

        }

        if ((!i.requested || rebind) &&i.apps.size() > 0) {

            try {

                bumpServiceExecutingLocked(r,execInFg, "bind");

                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

                r.app.thread.scheduleBindService(r,i.intent.getIntent(), rebind,

                        r.app.repProcState);

                if (!rebind) {

                    i.requested = true;

                }

                i.hasBound = true;

                i.doRebind = false;

            } catch(TransactionTooLargeException e) {

       ……

            }

        }

        return true;

   }

 

由於AMS中已經儲存了系統中已啟動的應用和服務的資訊,這裡的r.app.thread是服務的一個proxy,通過這個binder呼叫,服務中的scheduleBindService方法被執行,裡面呼叫了

        public final void scheduleBindService(IBindertoken, Intent intent,

                boolean rebind, intprocessState) {

            updateProcessState(processState,false);

            BindServiceData s = new BindServiceData();

            s.token = token;

            s.intent = intent;

            s.rebind = rebind;

 

            sendMessage(H.BIND_SERVICE, s);

        }

 

處理該訊息時呼叫ActivityThread的

 

   private void handleBindService(BindServiceData data) {

        Service s = mServices.get(data.token);

 

        if (s != null) {

            try {

               data.intent.setExtrasClassLoader(s.getClassLoader());

               data.intent.prepareToEnterProcess();

                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,SERVICE_DONE_EXECUTING_ANON, 0, 0);

                    }

                    ensureJitEnabled();

                } catch (RemoteException ex) {

                    throwex.rethrowFromSystemServer();

                }

            } catch (Exception e) {

            }

        }

   }

 

在server程序中,通過呼叫AMS服務publishService把binder物件傳給AMS

 

這裡,再回顧下這個時序圖

 

 

可以看出,程序間的呼叫都是binder通訊呼叫。

 

 

 

在AMS呼叫scheduleBindService的時候,有一個很巧妙的設計,就是把RecordService作為引數傳遞給了server端,其實server端並沒有實際使用RecordService,只是把RecordService再通過publishService又傳遞給了AMS,為什麼這麼曲折呢?

因為server端的sheduleBindService方法中進行了非同步呼叫。採用這種方式,連線了RecordService和server端傳遞的服務binder的對應關係。AMS根據這個RecordService找到呼叫的client端,再通過呼叫connected方法把server的binder傳送過去。

 

ActivityManagerService中有方法

   public void publishService(IBindertoken, Intent intent, IBinder service) {

        // Refuse possible leaked filedescriptors

        if (intent != null &&intent.hasFileDescriptors() == true) {

            throw newIllegalArgumentException("File descriptors passed in Intent");

        }

 

        synchronized(this) {

            if (!(token instanceofServiceRecord)) {

                throw newIllegalArgumentException("Invalid service token");

            }

            mServices.publishServiceLocked((ServiceRecord)token,intent, service);

        }

   }

 

ActiveServices.java

   void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {

                ...

 

                    for (int conni=r.connections.size()-1;conni>=0; conni--) {

                       ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);

                        for (int i=0;i<clist.size(); i++) {

                            ConnectionRecord c= clist.get(i);

 

                            ...

 

                            try {

//binder呼叫,AMS呼叫client的方法,把service的binder傳過去

                                c.conn.connected(r.name, service);

                            } catch (Exceptione) {

                                Slog.w(TAG,"Failure sending service " + r.name +

                                      " toconnection " + c.conn.asBinder() +

                                      "(in " + c.binding.client.processName + ")", e);

                            }

                        }

                    }

               ...

   }

這裡根據ServiceRecord找到對應的IServiceConnection

ServiceRecord中有定義

//IBinder -> ConnectionRecord of all bound clients

finalArrayMap<IBinder, ArrayList<ConnectionRecord>> connections

        = new ArrayMap<IBinder,ArrayList<ConnectionRecord>>();

由於一個service中可能會提供多個binder服務,每個binder又會有多個客戶端連線,所以這裡使用了ArrayMap<IBinder,ArrayList<ConnectionRecord>>的定義,

再通過遍歷connections找到對應的ConnectionRecord,ConnectionRecord中儲存有客戶端的IServiceConnection conn作為通訊proxy。

 

 

ConnectionRecord.java

final class ConnectionRecord {

   final AppBindRecord binding;    //The application/service binding.

   final ActivityRecord activity;  //If non-null, the owning activity.

   final IServiceConnection conn; // The client connection.