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方法时,向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) {
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 方法调用流程图如下,
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);
}