这篇主要讲BindService的主要逻辑 Api-27
bindService相比startService无非就是多了个可以持有Service的引用,那我们就针对这个功能作为切入点.
先看下BindService的使用流程
假设一个实现了AIDL的接口IDoSth.aidl
interface IDoSth {
void doSth();
}
调用流程:
Context.bindService(intent, connection, Service.BIND_AUTO_CREATE);
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IDoSth currentService = IDownloadService.Stub.asInterface(service);
try {
currentService.doSth();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
...
}
};
开始源码走起.
bindservice的入口为ContextImpl.bindService(Intent service, ServiceConnection conn, int flags);
然后ContextImpl.bindServiceCommon().
bindService和startService使用区别在于启动者是可以持有Service的渠道的,这个渠道就是IServiceConnection
ContextImpl:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {
IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
// 拒绝隐式Intent
validateServiceIntent(service);
try {
...
//3.调用服务端bindService
int res = ActivityManager.getService().bindService(..., sd, ...);
...
}
}
在bindServiceCommon这个方法里先是根据conn去获得IServiceConnection sd.sd最终获得的是LoadedApk的一个静态内部类InnerConnection.这个InnerConnection继承了一个Stub,理解过AIDL都知道这是一个用于进程间通信的Binder.
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);
}
}
}
InnerConnection的connected方法是去调用ServiceDispatcher的doConnected()方法,然后就调用ServiceConnection.onServiceConnected(),这样一个正常的绑定Service的流程就结束了.先得出一个结论,那就是IServiceConnection是用于服务端回调客户端的接口,最终服务端会把IBinder传回给客户端让客户端持有服务端Binder的句柄,即上述代码:
IDoSth currentService = IDownloadService.Stub.asInterface(service);//service就是IBinder
准备好IServiceConnection之后,就去调用服务端AMS的bindService()
服务端:
ActivityManagerService:
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
...
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
...
}
这里的mServices就是AMS中用于管理Service启动的ActiveServices
ActiveServices:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
...
//1.获得要启动的Service信息
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
...
ServiceRecord s = res.record;
...
//2.得到AppBindRecord,AppBindRecord描述的作用是连接Service和他的绑定者(IntentBindRecord)
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
//3.启动Service
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
return 0;
}
}
...
//4.service如果准备好之后就可以调用ServiceConnection.connected()了
if (s.app != null && b.intent.received) {
//Service已经运行, 我们可以立即发布connection.
try {
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
...
}
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
//5.
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
}
bindServiceLocked()这个方法里大致是这么个逻辑:
- 首先通过retrieveServiceLocked()获得将要启动的Service信息
- 然后去找IntentBindRecord,AppBindRecord.这里的操作是去做一系列绑定信息的添加.
- 这里如果目标Service还没启动,就会调用bringUpServiceLocked()去启动Service并且返回非空,那么就直接return. 0,下面就不用执行了,原因是启动Service是个异步操作,这样代码就不好继续下去.
- 这里有两个判断,一个是s.app != null && b.intent.received.这个received是在publishServiceLocked()之后才会设为true.所以第一次发布会跳过4,进入5
- requestServiceBindingLocked()会先调r.app.thread.scheduleBindService()进入客户端调用ActivityThread.handleBindService()然后又会回到服务端,最终回到ActiveServices.publishServiceLocked().在这个方法里才把received设为true并调用c.conn.connected()最终触发ServiceConnection.onServiceConnected()这个开头有分析过.
ActiveServices:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
try {
...
b.requested = true;
b.received = true;
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++) {
...
try {
c.conn.connected(r.name, service, false);
} catch (Exception e) {
}
...
}
}
...
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
...
return true;
}
至此,这就是BindService的持有引用的过程.