移步系列Android跨進程通信IPC系列
app是沒有權限向ServiceManager注冊service的。
bindService要負責找到service的代理binder并傳遞到app進程。這期間涉及到binder實體的跨進程傳輸,也就是所謂的匿名binder。因為這些binder并沒有在ServiceManager中注冊。
- 對于通過startService啟動的Service,只能作為接收方,啟動方可以通過onStartCommand來傳遞參數。這種啟動方式其可以有多個啟動者,不過在銷毀的時候,一旦有任意一個啟動調用了stopService或者自身stopSelf后,該Service就會停止,而啟動者的生命周期無法影響到該Service
- 對于通過bindService啟動的Service,其和啟動方有個“綁定”的過程,啟動方可以通過Service的binder引用來調用Service的方法。不過這種方式Service的生命周期會關聯著啟動方的,啟動方生命周期結束后,會默認unbindService來結束。
- bindService的過程要比startService的過程復雜一些,因為bingService之后,發起者可以跨進程調用service的某些方法。
- 使用bindService啟動的service的開發過程中一般都會借助aidl。
1 ServiceConnection
- 當一個app進程bindService()時,它需要先準備好一個實現了ServiceConnection接口的對象。
- ServiceConnection的定義如下:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
bindService()見名知意即綁定服務,建立一個邏輯連接。當連接建立之后,AMS會回調ServiceConnection接口對象的onServiceConnected()方法。把遠端service的代理binder傳遞過來,這樣就可以通過這個代理binder跨進程調用service中的方法了。
ServiceConnection接口的onServiceDisconnected()方法并不會在unbindService()操作斷開邏輯連接時執行。而是在遠端service進程終止時,AMS才會回調onServiceDisconnected()。
另外要注意的是縱然service進程的因為某些原因被終止,之前的綁定在該service上的邏輯連接仍處于激活狀態,當service再次運行的時候,AMS會回調onServiceConnected。
-
bindService的過程,我們要關心的binder有兩個
- 一個是遠端service的binder,有了它客戶端進程才能跨進程調用service中的方法;
- 另一個是binder是客戶端傳遞給AMS的,有了這個binder,AMS才會在邏輯連接建立之后,跨進程調用客戶端中的ServiceConnection接口的對象中的onServiceConnected()和onServiceDisconnected()。
另外bindservice在建立連接時,如果發現service還沒啟動,會根據flag是否設置BIND_AUTO_CREATE,決定是否啟動這個service。
同startService()一樣,bindService()也是一個異步的過程,也就是說當該方法返回時,邏輯連接很可能還沒有建立,通俗的說就是該方法返回時,onServiceConnected()方法很可能還沒執行。
2 bindService的過程
整個過程大體上是這樣的:
- 檢查要綁定的額service是否啟動,沒有的話,要先啟動service,然后執行service的生命周期方法。
- 執行綁定,先將遠端service的binder傳遞到AMS中,然后AMS在將其傳遞到客戶端組件進程中
3 bindService的過程分析
ContextImpl.bindService():
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
ContextImpl.bindServiceCommon():
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
IServiceConnection sd;
................
if (mPackageInfo != null) {
//從LoadedApk中拿到一個binder實體
//首先會把ServiceConnection轉成Binder對象
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
// Android5.0之后不允許使用隱式調用
// 這里對傳入的intetn進行檢查,如果是android5.0以上,
//是隱式intent的話拋出異常
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
....................
service.prepareToLeaveProcess();
// 向AMS發起跨進程調用其bindService方法
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
..............................
return res != 0;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
- 這里便引出了第一個binder:IServiceConnection sd。
- 這是一個實體binder,也就是說跨進程傳輸到AMS進程之后,AMS進程中最終得到的是一個代理binder。而且binder實體跨進程傳輸過程中,會在binder驅動層中為其創建相應的數據結構,這樣binder便在binder驅動中扎根了。
- 那么這個binder實體最初是從哪里來的呢?
3.1 LoadedApk.getServiceDispatcher()
- 此處的mPackageInfo是用戶進程里和apk對應的LoadedApk對象
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
// 先從LoadedApk.mService中查找,
// 看看發起綁定操作的組件是否已經存在一個用來處理傳入的
//ServiceConnection接口對象的ServiceDispatcher
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
// 存在的話直接返回這個ServiceDispatcher
sd = map.get(c);
}
if (sd == null) {
// 不存在的話創建一個ServiceDispatcher
// 這里要注意第三個參數handler,是主線程的handler
// 保存在了ServiceDispatcher.mActivityThread中
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
// 將創建的ServiceDispatcher緩存到LoadedApk.mService
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();
}
}
- LoadedApk.ServiceDispatcher從它的名字中可以看出它類似于一個Dispatcher的作用,這個類的作用就是當client bind某個service成功之后,負責向client分配service IBinder的;以及當client unbind service時,負責通知client unbind service的狀態。
- 對于ServiceDispatcher的管理,是以apk,即package為載體的,也就是說對于某個package,定義在其中的component如果請求去bind一個service,那么LoadedApk將為這個component分配一個ServiceDispatcher。
- component每請求bind一個service,都會為其指定一個ServiceConnection接口的對象,同一component組件內,LoadedApk將會為這個ServiceConnection接口的對象分配一個唯一的ServiceDispatcher。
- LoadApk會為每個context保存一個key為ServiceConnection、value為ServiceDispatcher的map,這樣盡量做到了ServiceConnection對應的Binder對象的復用。如果沒有可復用,則把ActivityThread的主Handler和ServiceConnection對應起來,這樣在復用的時候,ServiceDispatcher會調用validate方法檢查handler為當前主線程handler,保證不錯亂。
-
每個app進程中是可以加載多個apk包的,記錄在ActivityThread.mPackages,整個關系如下所示:
2780242-c11bbd267ebbd52a.png - packages中的組件component,例如某個activity中使用bindService去綁定一個service時,都需要提供一個實現ServiceConnection接口的對象,每一個這樣的對象都會與一個ServiceDispatcher對象綁定。
- LoadedApk.mServices中存儲了本package中所有組件中所有的ServiceDispatcher。它是一個ArrayMap,key是context,實際上就是組件,因為組件繼承自Context。
- value又是一個ArrayMap,因為一個組件中使可以綁定多個service的嘛。這個map的可以是實現了ServiceConnection接口的對象,value是與之綁定的ServiceDispatcher對象。
3.1.1 ServiceDispatcher
static final class ServiceDispatcher {
// 一個binder實體對象
private final ServiceDispatcher.InnerConnection mIServiceConnection;
// 邏輯連接建立時,執行的回調接口對象
private final ServiceConnection mConnection;
// 所在的組件,即client
private final Context mContext;
// 進程主線程即UI線程中的handler
private final Handler mActivityThread;
private final ServiceConnectionLeaked mLocation;
// 綁定service時,傳入的flag,例如BIND_AUTO_CREATE
private final int mFlags;
private RuntimeException mUnbindLocation;
private boolean mDied;
private boolean mForgotten;
// 因為一個ServiceConnection可以被bindService方法多次調用,用來啟動不同的service,
// 那么ServiceDispatcher中自然要存儲這些連接信息了:
// ConnectionInfo很簡單只有兩個屬性成員
// IBinder binder;遠端service的引用binder
// IBinder.DeathRecipient deathMonitor; 遠端binder的死亡通知方法
private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
= new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
................
}
ServiceDispatcher.InnerConnection是一個繼承自 IServiceConnection.Stub的binder實體類:
3.1.2 InnerConnection
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) {
// 這里的mActivityThread是主線程的handler
// 也就是說,實際上ServiceConnection中的回調是在主線程中執行的
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0));
} else {
doConnected(name, service);
}
}
- ServiceDispatcher.InnerConnection是一個繼承自 IServiceConnection.Stub的binder實體類,從其名字上來看是一個內部連接,該怎么理解呢?
- 因為bindService()傳遞的ServiceConnection接口對象,并沒有跨進程傳輸到AMS中,從bindServiceCommon()方法可以看出最終傳遞到AMS的是LoadedApk.getServiceDispatcher()返回的ServiceDispatcher.InnerConnection。AMS通過它跨進程間接調用ServiceConnection中的方法。
- 因為一個ServiceConnection可以在同一個客戶端組件內被bindService方法多次調用,用來綁定不同的service,那么ServiceDispatcher中自然要存儲這些使用同一個serviceConnection綁定的service的連接信息了——ServiceDispatcher.mActiveConnections。
3.1.3 ConnectionInfo
- 它是ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>類型對象,
private static class ConnectionInfo {
// 遠端service的代理binder
IBinder binder;
// 遠端service 死亡通知回調
IBinder.DeathRecipient deathMonitor;
}
3.2 AMS中綁定service過程
- ContextImpl.bindServiceCommon()方法中得到ServiceConnection對應的ServiceDispatcher對象之后,便向AMS發起請求,跨進程調用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) {
// mServices是ActiveService
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
- AMS.bindService()對參數做了簡單檢查之后,又調用ActiveService.bindServiceLocked()方法。
- 這里先對ActiveService.bindServiceLocked()方法的幾個重要參數做一些說明:
int bindServiceLocked(
IApplicationThread caller, // 客戶端進程ActivityThread.mAppThread的代理binder
IBinder token,
Intent service,// 客戶端綁定service時的intent
String resolvedType,
IServiceConnection connection, //客戶端進程的中ServiceDispatcher.InnerConnection的代理binder
int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
ActiveService.bindServiceLocked()startService的ActiveService.startServiceLocked()方法中有很多相似的邏輯。
- 要先查找要啟動的servicee在AMS中的代表ServiceRecord是否存在,存在的話,意味著service已經啟動;不存在的話,要創建一個ServiceRecord對象,這些操作還是由retrieveServiceLocked()方法負責的。
- 然后通過bringUpServiceLocked()調用service的生命周期方法。只不過bindservice除了這兩個基本操作外,還要執行綁定操作,即將service的binder傳跨進程傳輸到app客戶端組件中。
3.2.1 bindServiceLocked綁定前的準備工作
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
...............
// 得到service對應的ServiceRecord
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
ServiceRecord s = res.record;
....
// 根據傳入的intent和發起者進程,查找到一個合適的AppBindRecord對象,
// 查找不到就創建一個,下面會介紹規則
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
// 為本次連接創建ConnectionRecord對象
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
// 客戶端進程的中ServiceDispatcher.InnerConnection的代理binder
IBinder binder = connection.asBinder();
// 將創建的邏輯連接對象ConnectionRecord,記錄在ServiceRecord中
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
// 同時記錄在AppBindRecord.connections中
b.connections.add(c);
// AppBindRecord.client是ProcessRecord,代表客戶端的進程
b.client.connections.add(c);
.......
// 除了ServiceRecord.connections記錄了該service連接信息外
// ActiveService.mServiceConnections記錄了當前系統中所有的連接信息
// 所以也要將創建的連接對象,加入ActiveService.mServiceConnections
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);
............
在前面關于service機制介紹的文章中提到過ServiceRecord中有一些數據成員是bindservice時用到的:
final class ServiceRecord extends Binder {
...............
final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
= new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();// IBinder -> ConnectionRecord of all bound clients
// service所在的進程
ProcessRecord app; // where this service is running or null.
}
AMS描述綁定service時的intent的IntentBindRecord類
- app客戶端組件中發起綁定service操作時,一定要用到intent,一般是顯示的intent(android 5.0 之后要求必須是顯示intent),而且只設置intetn.mComponent字段。
- AMS中對綁定操作傳入的intent是用Intent.FilterComparison類來描述,也就是說當客戶端綁定service時使用的intent指定的參數都一致的話,AMS會將其看做是同一類intent。
- AMS會為這類intent為其創建一個IntentBindRecord對象。
final class IntentBindRecord {
// 綁定的service在AMS中的代表
final ServiceRecord service;
/** The intent that is bound.*/
final Intent.FilterComparison intent; //
/** All apps that have bound to this Intent. */
final ArrayMap<ProcessRecord, AppBindRecord> apps
= new ArrayMap<ProcessRecord, AppBindRecord>();
// service的binder代理binder
IBinder binder;
其中IntentBindRecord.service 最終會保存綁定的service的ServiceRecord;
- IntentBindRecord.intent 就是前面所說的綁定service時設定的參數一致的intent在AMS中的表示Intent.FilterComparison對象;
- IntentBindRecord.apps用來記錄所有使用該類intetn綁定同一個service的客戶端信息。key是客戶端進程ProcessRecord,value是AppBindRecord。因為不同的客戶端可能使用相同的intent參數來綁定同一個service,所以IntentBindRecord要記錄下這些客戶端信息;
- IntentBindRecord.binder最終保存service的代理binder;
- AMS通過ServiceRecord.retrieveAppBindingLocked()方法為判斷是否為傳入的intent創建一個IntentBindRecord:
3.2.1.1 ServiceRecord.retrieveAppBindingLocked()
public AppBindRecord retrieveAppBindingLocked(
Intent intent,// 客戶端發起綁定操作時傳入的intent
ProcessRecord app // 客戶端組件進程) {
// 為傳入的intent創建一個Intent.FilterComparison
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
// 查找傳入的intent是否已經有IntentBindRecord
IntentBindRecord i = bindings.get(filter);
if (i == null) {
// 沒有的話創建
i = new IntentBindRecord(this, filter);
//并緩存到ServiceRecord.bings中
bindings.put(filter, i);
}
// 查找客戶端的組件是否已經綁定過該service
AppBindRecord a = i.apps.get(app);
// 綁定過的話,返回找到的AppBindRecord
if (a != null) {
return a;
}
// 沒有的話創建一個AppBindRecord對象
a = new AppBindRecord(this, i, app);
// 并緩存到IntentBindRecord.apps中
i.apps.put(app, a);
return a;
}
retrieveAppBindingLocked()另一個主要作用是查找并創建AppBindRecord對象。
AMS用于描述綁定service的客戶端整體信息的AppBindRecord類
- 對于一個Service來說,有多少app客戶端進程和它建立了綁定關系,就會有多少個AppBindRecord對象。一個app客戶端進程里可以有多個地方發起綁定動作,所以AppBindRecord里需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接對象。
final class AppBindRecord {
// 所在的service
final ServiceRecord service; // The running service.
// 客戶端發起的bindservice時傳入的intent,AMS會為其創建一個對應的IntentBindRecord
final IntentBindRecord intent; // The intent we are bound to.
// 客戶端進程
final ProcessRecord client; // Who has started/bound the service.
// 客戶端所在的app,其他組件綁定該service的邏輯連接
final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// All ConnectionRecord for this client.
- AppBindRecord.service 用于描述客戶端綁定的service;
- AppBindRecord.intent 用于描述客戶端綁定該service時使用的intent;
- AppBindRecord.client 用于描述客戶端的進程;
- AppBindRecord.connections 用于描述客戶端中所有組件綁定該service時創建的邏輯連接;
AMS通過ServiceRecord.retrieveAppBindingLocked()來查找并創建一個合適的AppBindRecord對象。
3.2.1.2 為本次連接創建ConnectionRecord對象
- AMS為每次綁定過程中創建的連接分配一個ConnectionRecord類型對象.
- ConnectionRecord用來描述一個連接信息,即綁定信息,要對客戶端和service端進行描述。
ConnectionRecord用來描述一個連接信息,即綁定信息,要對客戶端和service端進行描述。
- ConnectionRecord.binding代表這個連接所在的AppBindRecord.
- ConnectionRecord.coon是一個binder代理對象,其實體binder是app進程中ServiceDispatcher.mIServiceConnection。用來回調客戶端進程中當連接成功建立時的回調方法。
- 客戶端組件在bindservice時,都要創建一個實現ServiceConnection接口的對象,每個這樣的對象在客戶端組件所在的LoadedApk中都會分配一個ServiceDispatcher對象,這個對象用于處理所有使用該ServiceConnection接口的對象綁定service的客戶端組件的回調方法的執行。
- ConnectionRecord.flags 用于描述綁定該service時,指定的flags,例如BIND_AUTO_CREATE.
- AMS創建的ConnectionRecord對象會存儲在ServiceRecord.connections成員變量中.
- ServiceRecord.connections是一個map,key是ServiceDispatcher.InnerConnection的代理binder,value是ArrayList<ConnectionRecord>類型的。
- 因為客戶端進程中一個Component中可能使用同一個ServiceConnection接口對象來多次綁定同一個service,因為每次綁定都會創建一個ConnectionRecord對象,那么這些ConnectionRecord需要使用ArrayList<ConnectionRecord>來保存。
- value中ConnectionRecord.IServiceConnection實際上一致與key是一致的。
- AMS除了將這個"邏輯連接"ConnectionRecord對象,記錄在ServiceRecord.connections中外,還要向至少下面的幾處位置做記錄:
- 客戶端所在的進程在AMS中的代表ProcessRecord.connections
- ActiveService.mServiceConnections,這里面記錄了AMS中所有app的service的連接
- AppBindRecord.connections中
之所以要在這么多地方做記錄,可能是為了在不同的場合下迅速查找到連接吧。
了解了以上內容后,就可以通過下圖簡明的描述客戶端進程和AMS之間的關系:
3.2.2 bringUpServiceLocked 執行service生命周期方法
在做好前面的準備工作之后,binderservice()就開始準備與Service建立連接了。那么自然要先對service進行一些操作,說白了就是執行service的生命周期方法,這是由bringUpServiceLocked()方法來負責的。
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
.................
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
// 只有當要綁定的service所在的進程還啟動的時候,該方法返回非null
// 因為啟動進程需要一段時間,所以就要先退出來
// 這里暫時假設service所在的進程已經啟動
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
....................
此時可以分為兩大情況:
- 1.service進程還沒啟動
- 2.service進程已經啟動,此時有可分為兩種情況:service還沒啟動和service已經運行。bringUpServiceLocked()方法依據ServiceRecord.app區分以上兩大情況:
- 1.ServiceRecord.app不為null,而且ServiceRecord.app.thread也不為null,預示著service已經運行了,但是這時候并不會向startService()啟動service那樣跨進程調用service.onStartCommand()生命周期方法,因為bindService啟動service時沒有將信息記錄到ServiceRecord.pendingStarts。
- .ServiceRecord.app為null,說明ServiceRecord還沒有和service所在的進程關聯。此時在依據ServiceRecord.processName,也就是service要求運行在的進程的名字,在AMS中查找是否有這樣的進程存在,如果有的話,只需要啟動service,也就是在找到的進程中創建service對象,并執行service.onCreate()生命周期方法。
如果沒有在AMS中找到名字為ServiceRecord.processName的進程,那么就要先創建進程了,這里不考慮這種情況。
3.2.2.1 綁定service bindServiceLocked
綁定實際上就是想辦法拿到service的binder,并將其傳遞到客戶端組件進程,另外還要對前面準備工作期間創建的數據結構設置相關的字段。
這部分代碼如下:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
.................
// b.intent.received為true,表明已經拿到了service的binder
f (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
// 這里就可以直接遠程調用客戶端組件中onServiceConnected()方法將service的binder傳遞過去了
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
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);
}
getServiceMap(s.userId).ensureNotStartingBackground(s);
} finally {
Binder.restoreCallingIdentity(origId);
}
這里分兩種情況:
- 1.客戶端組件之前已經綁定過該service,現在這個組件又要再次綁定,也就是客戶端同一組件重復綁定
- 2.客戶端組件首次綁定該service
這兩種情況的區分是依據AppBindRecord.intent,即AMS為bindservice()傳入的intent分配的IntentBindRecord對象來決定的。
final class IntentBindRecord {
/** Binder published from service. */
IBinder binder;
/** Set when we have initiated a request for this binder. */
boolean requested;
/** Set when we have received the requested binder. */
boolean received;
/** Set when we still need to tell the service all clients are unbound. */
boolean hasBound;
}
- IntentBindRecord.received為true,表明IntentBindRecord.binder已經指向遠端service的binder;
- IntentBindRecord.requested為false,表明IntentBindRecord.binder還沒有指向遠端service的binder;
這里分析第二種情況,時序圖如下:
- requestServiceBindingLocked()方法中跨進程調用service所在的進程中方法,最終會導致service.onBind()執行,該方法返回service的實體binder。
- 這里有一點要貼別注意,那只要service被某個客戶端組件綁定過了,就不會再執行service.onBind()方法了,原因就在requestServiceBindingLocked()方法中:
3.2.2.2 requestServiceBindingLocked
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
..............
// 傳入的rebind為false還是true,取決于AMS調用service.onUnbind()返回值
// 如果希望客戶端下一次綁定到服務時接收 onRebind() 調用(而不是接收 onBind() 調用),onUnbind()返回true
// service首次被綁定時,rebind肯定為false
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
// 跨進程調用service所在進程的scheduleBindService()方法,執行綁定操作,這是一個異步方法,會立即返回
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
// 只要service被綁定過了,IntentBindRecord.requested就會被設置為true
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
.................
} catch (RemoteException e) {
.................
}
}
return true;
}
- 通過以上代碼可知,只要service被綁定過一次,那么IntentBindRecord.requested救回被設置為true,其他客戶端組件再次綁定的時候,由于if條件為假,所以不會再次跨進程調用service進程中的scheduleBindService()方法,也就不會調用service.onBind()方法了。
- 因為bindservice時傳入的intent一般都是顯示intent,不會設置其他參數,所以只要客戶端綁定的是同一個service,那么在AMS中只會有一個IntentBindRecord對象。
- 除非該servcie綁定過之后,所有綁定它的客戶端組件都執行了unbindService(),那么最終會導致service.onUnbind()方法執行,如果該方法返回了true,那么下次再有客戶端組件綁定該service時,rebind會被設置為true,這導致不會調用service.onBind(),而是調用service.rebind()方法。
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (DEBUG_SERVICE)
Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
// 傳入flase時執行onBind()
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
// 傳入true時執行onRebind()
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
這里考慮首次綁定時的情況,所以rebind肯定為false,那么service調用過onBind()之后,又通過AMS的代理,跨進程調用AMS的publishService()將service的binder傳遞到AMS中,然后在傳遞到客戶端。
3.2.2.3 publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
.......
IntentBindRecord b = r.bindings.get(filter);
// 初次綁定
if (b != null && !b.received) {
// 保存service的代理binder
b.binder = service;
// 設置下面的兩個標志為true
b.requested = true;
b.received = true;
// 一般情況下,首次綁定時,connections.size為1
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);
if (!filter.equals(c.binding.intent.intent)) {
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Not publishing to: " + c);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Published intent: " + intent);
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
try {
// 將service的binder傳遞到客戶端
c.conn.connected(r.name, service);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.name +
" to connection " + c.conn.asBinder() +
" (in " + c.binding.client.processName + ")", e);
}
}
}
}
...........
3.2.2.4 bindServiceLocked
那么當首次綁定之后,又有其他客戶端組件來綁定這個service,那么在bindServiceLocked()方法中,會直接跨進程調用客戶端的connected():
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
................
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
// 直接跨進程調用客戶端的connected()
// 不在需要跨進程調用service.onBind()
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
..........................
}
在看客戶端進程的connected()
3.2.2.5 connected()
public void connected(ComponentName name, IBinder service) throws RemoteException {
// 先得到處理ServiceConnection的ServiceDispatcher
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
public void connected(ComponentName name, IBinder service) {
// mActivityThread是在創建 ServiceDispatcher對象時,傳入的組件所在進程的主線程的handler
// 也就是說RunConnection是在組件所在的主線程中執行的
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0));
} else {
doConnected(name, service);
}
}
RunConnection.run()中會調用LoadedApk.doConnected()方法:
public void run() {
if (mCommand == 0) {
doConnected(mName, mService);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
3.2.2.6 LoadedApk.doConnected():
public void doConnected(ComponentName name, IBinder service) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
.....................
ServiceDispatcher.ConnectionInfo old;
//很有意思,也就是說同一組件中對同一個service重復綁定,onServiceConnected()只會執行一次
old = mActiveConnections.get(name);
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
if (service != null) {
// A new service is being connected... set it all up.
mDied = false;
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
// 設置死亡回調
service.linkToDeath(info.deathMonitor, 0);
// 將info保存在LoadedApk.mActiveConnections
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
mActiveConnections.remove(name);
return;
}
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}
// If there was an old service, it is not disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
// If there is a new service, it is now connected.
if (service != null) {
// 執行回調
mConnection.onServiceConnected(name, service);
}
}
doConnected()比較有意思的是下面的代碼:
ServiceDispatcher.ConnectionInfo old;
old = mActiveConnections.get(name);
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
mActiveConnections來自ServiceDispatcher:
private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
= new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
ServiceDispatcher.mActiveConnections用來記錄該LoadedApk中的組件所綁定的service的連接信息。key是service的組件名,value是ServiceDispatcher.ConnectionInfo。
ConnectionInfo中記錄了service的代理binder以及死亡通知回調。
private static class ConnectionInfo {
// 遠端service的代理binder
IBinder binder;
// 遠端service 死亡通知回調
IBinder.DeathRecipient deathMonitor;
}
connected(ComponentName name, IBinder service)方法的第一個參數是service的組件名,第二個參數是service的代理binder。
- connected()方法中首先以name為key在mActiveConnections查找,如果索引到的ConnectionInfo的binder與傳入的binder一致的話,說明是同一組件內使用同一個ServiceConnection接口對象對同一個service重復綁定,此時不會執行onServiceConnected()方法。
4 unbindeService
現在在看一下unbindeService()的過程,直接看AMS.unbindServiceLocked(),整個過程大體上就是找到相關的連接對象ConnectionRecord,將其從相關的map中移除,然后根據情況決定是否調用serive.onUnbind生命周期方法
4.1 unbindServiceLocked
boolean unbindServiceLocked(IServiceConnection connection) {
// 得到ServiceDispatcher.InnerConnection的binder
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
// 前面說了ActiveService.mServiceConnections,這里面記錄了AMS中所有app的service的連接
// 自然也包括同一組件內使用同一個ServiceConnection綁定同一個service的情況
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
// 說明沒有使用該ServiceConnection接口對象綁定過service
// 所以無需unbind
if (clist == null) {
Slog.w(TAG, "Unbind failed: could not find connection for "
+ connection.asBinder());
return false;
}
final long origId = Binder.clearCallingIdentity();
try {
// 依次取出同一組件內使用該ServiceConnection接口對象綁定的service的連接信息對象ConnectionRecord
// 這里要注意的是,這相當于在發起unbindService()操作的組件中,對所有使用該ServiceConnection接口對象綁定的sercvice
// 發起unbindService操作
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
removeConnectionLocked(r, null, null);
if (clist.size() > 0 && clist.get(0) == r) {
// In case it didn't get removed above, do it now.
Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
clist.remove(0);
}
if (r.binding.service.app != null) {
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
r.binding.service.app.treatLikeActivity = true;
mAm.updateLruProcessLocked(r.binding.service.app,
r.binding.service.app.hasClientActivities
|| r.binding.service.app.treatLikeActivity, null);
}
mAm.updateOomAdjLocked(r.binding.service.app);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return true;
}
4.2 removeConnectionLocked():
void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
// 從ServiceRecord中取出所有綁定該service的連接
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist != null) {
// 將使用要unbindService()的ServiceConnection創建的連接
// 從ServiceRecord.connections中移除
clist.remove(c);
if (clist.size() == 0) {
s.connections.remove(binder);
}
}
// 將使用要unbindService()的ServiceConnection創建的連接
// 從AppBindRecord.connections中移除
b.connections.remove(c);
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
c.activity.connections.remove(c);
}
}
if (b.client != skipApp) {
b.client.connections.remove(c);
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.updateHasAboveClientLocked();
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
}
}
clist = mServiceConnections.get(binder);
if (clist != null) {
// 將使用要unbindService()的ServiceConnection創建的連接
// 從ActiveService.mServiceConnections中移除
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}
mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
// 如果AppBindRecord.connections.size為0
// 表示某客戶端已經沒有組件與該service綁定了
// 那么將客戶端從IntentBindRecord.apps中移除
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}
if (!c.serviceDead) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Disconnecting binding " + b.intent
+ ": shouldUnbind=" + b.intent.hasBound);
// 如果IntentBindRecord.apps.size為0,表示沒有客戶端與該service綁定了
// 那么開始回調service進程的scheduleUnbindService(),執行service.unbind()
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
bumpServiceExecutingLocked(s, false, "unbind");
if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
&& s.app.setProcState <= ActivityManager.PROCESS_STATE_RECEIVER) {
// If this service's process is not already in the cached list,
// then update it in the LRU list here because this may be causing
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
mAm.updateOomAdjLocked(s.app);
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
b.intent.doRebind = false;
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
serviceProcessGoneLocked(s);
}
}
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
if (!hasAutoCreate) {
if (s.tracker != null) {
s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
}
// 根據情況決定是否調用service.onDestroy()方法
bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
}
}
}
scheduleUnbindService()會導致下面的方法在service的主線程中執行:
4.3 scheduleUnbindService()
private void handleUnbindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
// 執行service.onUnbind()生命周期方法
boolean doRebind = s.onUnbind(data.intent);
try {
if (doRebind) {
// 如果onUnbind()的返回值設置為true的話,
// 調用AMS.unbindFinished()
ActivityManagerNative.getDefault().unbindFinished(
data.token, data.intent, doRebind);
} else {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to unbind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
unbindeService()代碼就說道這里,接下來總結下:
- ServiceConnection.onServiceDisconnected()并不會在unbindeService()過程中調用,它只會在service進程被終止時回調通知綁定的客戶端;
- 只有在綁定這個service的所有客戶端中的組件都執行了unbindeService()時,service才會被銷毀,執行servcice.onDestroy(),這也就要求暗示我們當完成于服務的交互后,最好unbindeService,這樣有利于系統及時回收service資源;
- 當目標service所在的進程被殺掉時(即除了正?;厥誷ervice),系統并不會銷毀之前的與該service綁定的組件創建的連接,一旦service后續再次運行,系統會再次回調onServiceConnected();
- 如果service是通過startService()啟動的,那么service將一直運行到其通過 stopSelf() 自行停止,或其他組件調用 stopService() 為止,無論其是否綁定到任何客戶端;
- unbindeService()操作也是異步操作的;
- 一般情況下,只要service被客戶端綁定過了,當其再被綁定時,不會在調用service.onBind()方法了,也就是說通常service.onBind()只會執行一次;
- 同一個組件內使用同一個ServiceConnection接口對象,重復綁定一個service時,會在AMS中為起創建連接,但是不會導致ServiceConnection.onServiceConnected()方法執行
- 同一個組件內執行unbindeService(ServiceConnection sc)時,相當于對所有使用sc綁定的service執行一次unbindeService操作;
這里比較有趣的是,假設同一組件內使用sc重復綁定的一個servcie N次,那么這一次unbindeService(),當對于該該組件綁定該service來說執行了N次unbindeService操作。
5 生命周期的總結:
- 單獨使用bindService(),unbindService()會經歷:->onCreate()->onBind()->Service running->onUnbind() -> onDestroy();
- 單獨使用startService(),stopService()會經歷:->onCreate()->onStartCommand()->Service running-> onDestroy();
-
先調用startService(),再調用bindService()方法:
- a. 如果結束只調用unbindService(),那么只會執行到onUnbind(),將不會執行onDestroy():->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind();
- b. 如果在unbindService后,在調用stopService(),那么:->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()->onDestroy();
service的生命周期方法都運行在主線程中,所以如果要在生命周期中執行耗時操作,請額外開啟線程。
參考
Android6.0之App的Service組件運行機制之bindService
Android 7.0 中 Service bind 流程詳解