移步系列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 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();
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,整个关系如下所示:
- 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 mActiveConnections
= new ArrayMap();
................
}
ServiceDispatcher.InnerConnection是一个继承自 IServiceConnection.Stub的binder实体类:
3.1.2 InnerConnection
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(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
类型对象,
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 clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList();
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();
mServiceConnections.put(binder, clist);
}
clist.add(c);
............
在前面关于service机制介绍的文章中提到过ServiceRecord中有一些数据成员是bindservice时用到的:
final class ServiceRecord extends Binder {
...............
final ArrayMap> connections
= new ArrayMap>();// 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 apps
= new ArrayMap();
// 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
记录下每个绑定动作对应的逻辑连接对象。
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 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
类型的。 - 因为客户端进程中一个Component中可能使用同一个ServiceConnection接口对象来多次绑定同一个service,因为每次绑定都会创建一个ConnectionRecord对象,那么这些ConnectionRecord需要使用ArrayList
来保存。 - 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 clist = r.connections.valueAt(conni);
for (int i=0; i
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 mActiveConnections
= new ArrayMap();
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 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 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所在的进程被杀掉时(即除了正常回收service),系统并不会销毁之前的与该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 流程详解