天天看點

bindService 分析---之一2, bindService

2, bindService

當一個使用者程序bindService()時,它需要先準備好一個實作了ServiceConnection接口的對象。ServiceConnection的定義如下:

public interface ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service);
    public void onServiceDisconnected(ComponentName name);
}
           

每當使用者調用bindService(),Android都将之視作是要建立一個新的“邏輯連接配接”。而當連接配接建立起來時,系統會回調ServiceConnection

接口的onServiceConnected()。另一方面,那個onServiceDisconnected()函數卻不是在unbindService()時發生的。一般來說,當目标

service所在的程序意外挂掉或者被殺掉時,系統才會回調onServiceDisconnected(),而且,此時并不會銷毀之前的邏輯連接配接,也就是說,

那個“邏輯連接配接”仍然處于激活狀态,一旦service後續再次運作,系統會再次回調onServiceConnected()。bindService調用流程圖如下,

bindService 分析---之一2, bindService

程序調用bindService方法時,向AMS請求這一段和啟動服務的流程一樣,在此就不詳細分析了。調用流程圖如下,

bindService 分析---之一2, bindService

AMS的bindService方法如下,

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

bindServiceLocked方法如下,

int bindServiceLocked(IApplicationThread caller, IBinder token,
    Intent service, String resolvedType,
    IServiceConnection connection, int flags, int userId) {
    . . . . . .
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    . . . . . .
    ActivityRecord activity = null;
    . . . . . .
        activity = ActivityRecord.isInStackLocked(token);
    . . . . . .
    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    . . . . . .
    ServiceRecord s = res.record;
    . . . . . . 
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);  
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);

        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
        . . . . . .
        clist.add(c);
        b.connections.add(c);
        . . . . . .
        b.client.connections.add(c);
        . . . . . .
        clist = mServiceConnections.get(binder);
        . . . . . .
        clist.add(c);

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            . . . . . .
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }
        . . . . . .
        if (s.app != null && b.intent.received) {
            . . . . . .
                c.conn.connected(s.name, b.intent.binder); 
            . . . . . .
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
            requestServiceBindingLocked(s, b.intent, callerFg, false);  
        }
    . . . . . .
}
           

盡管這個函數裡有不少技術細節,而且涉及到多個映射表,但它的總體意思大概是這樣的。每當使用者調用bindService()時,

Android都将之看作是要建立一個新的“邏輯連接配接”,而每個邏輯連接配接都對應一個ConnectionRecord節點,是以最終的表現肯定

是向ServiceRecord内部的某張映射表裡添加一個新的ConnectionRecord節點。當然,在實際運作時,這個節點還會記錄

進其他幾個映射表裡(比如系統總映射表)。

對于一個Service而言,有多少應用和它建立了綁定關系,就會有多少個AppBindRecord節點, 當然,一個應用裡可以有多個

地方發起綁定動作,是以AppBindRecord裡需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接配接節點。

在ConnectionRecord被記錄進合适的表後,要開始和目标service建立連接配接了。我們可以看到,bindServiceLocked()會嘗試

呼叫起目标service。

if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            . . . . . .
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }
           

看到這句if語句,大家應該知道調用bindService()時為什麼常常要加上BIND_AUTO_CREATE了吧:

bindService(intent, conn, Context.BIND_AUTO_CREATE);
           

這裡面也分為三種情況:

1,目标服務已經啟動了,直接調用c.conn.connected 方法

2,目标服務并沒有啟動,調用requestServiceBindingLocked

3,目标服務所在的程序還未啟動,調用bringUpServiceLocked方法

1.1 connected

connected 方法調用流程圖如下,

bindService 分析---之一2, bindService

c.conn.connected到底是怎麼調用的呢?此時在AMS服務程序中。

C是ConnectionRecord對象, conn 是 IServiceConnection,一看就是跨程序調用。

這時,以下流程都是運作于發起bindService所在的程序中,在LoadedApk的内部類 ServiceDispatcher裡,

private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher; 
            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

   public void connected(ComponentName name, IBinder service) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }
           

ServiceDispatcher的connected方法如下,

public void connected(ComponentName name, IBinder service) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0));
            } else {
                doConnected(name, service);
            }
        }
           

RunConnection類定義如下,

private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command) {
                mName = name;
                mService = service;
                mCommand = command;
            }

            public void run() {
                if (mCommand == 0) {
                    doConnected(mName, mService);
                } else if (mCommand == 1) {
                    doDeath(mName, mService);
                }
            }

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
        }
           

doConnected方法如下,

public void doConnected(ComponentName name, IBinder service) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {
               
           •••
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }
           

到這兒終于看到調用onServiceConnected方法了,在該方法中,有一個 service參數,這就是被綁定的binder的引用,

通過它就可以進行跨程序調用。

public void doDeath(ComponentName name, IBinder service) {
            mConnection.onServiceDisconnected(name);
        }
           

繼續閱讀