震惊!Binder机制竟恐怖如斯!(下)
前言
上文介绍了AIDL的基本使用,并通过源码分析了AIDL实现IPC的原理。下面我们来介绍Binder在系统进程中的使用。
源码分析
我们在Activity中通过调用bindService()
来完成远程Service的绑定,查看源码,可以发现真正调用该方法的是ContextImpl
类。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
在bindService中,首先通过warnIfCallingFromSystemProcess()
进行了一次安全性的判断,据说这里面有很多文章,挖个坑吧先//TODO。接着调用了bindServiceCommon()
方法,该方法重点代码如下:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,UserHandle user) {
...省略
ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(),
getActivityToken(),
service.resolveTypeIfNeeded(
getContentResolver()),
sd, flags, getOpPackageName(),
user.getIdentifier());
...省略
}
这里面出现了一个ActivityManagerNative
类,这是什么呢?点进去看看
public abstract class ActivityManagerNative extends Binder implements IActivityManager
可见,ActivityManagerNative继承了Binder实现了IInterface,这和上文中我们自己实现的AIDL类相同,所以说,ActivityManagerNative也是一个AIDL通信的工具!
继续分析代码,AMN中调用了getDefault()
方法,源码如下:
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
首先这是一个单例模式,最终返回了一个叫am的对象。
其中,IBinder b = ServiceManager.getService("activity")
通过ServiceManager获取了一个系统服务的引用。
这里我们拓展一下ServiceManager。
如图所示,在Android中,有许多系统级的服务,在应用层可以通过调用系统服务很轻松的实现一些功能。而ServiceManager就相当于一个中介,可以将系统服务的引用以Ibinder的形式返回给相应的应用。这也是系统中AIDL的实现。
在上面的getDefault()方法中,我们获取了系统服务的IBinder引用,之后就通过IActivityManager am = asInterface(b)
来获取系统服务的具体对象——ActivityManagerService,即返回的am就是ActivityManagerService。
要注意的是,到此为止,代码已经不在客户端进程中运行了,而是在系统进程中运行。
我们查看ActivityManagerService类的bindService()
方法,发现它又调用了ActiveServices类的bindServiceLocked()
方法,该方法重点代码如下:
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
...省略
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
c.conn.connected(s.name, b.intent.binder);
}
...省略
// 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);
}
...省略
}
}
这里面的ArrayList< ConnectionRecord>用来保存客户端回调用的ServiceConnection,真正的回调会在c.conn.connected(s.name, b.intent.binder)
中执行。注意这行代码的注释,是说如果service已经启动,就在这里直接调用c.conn.connected(s.name, b.intent.binder)
完成回调,流程结束。那如果还没有启动呢?我们继续往下看。
如果还没有启动service,代码会执行requestServiceBindingLocked()
方法,查看该方法,代码量很少,可以发现其中最重要的一句代码是
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
- r是ServiceRecord类,用来保存系统服务的信息
- app是ProcessRecord类,用来进程记录
- thread是IApplicationThred接口,是整个应用组件启动的核心类,也就是ApplicationThread类。ApplicationThread是ActivityThread的内部类。要注意的是,这里已经启动了远程进程的ActivityThread(当然如果service是本地的,就会直接创建并且注册到IBinder内存中,不会绕这么远)
查看其中的scheduleBindService
方法:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
sendMessage(H.BIND_SERVICE, s);
}
发现关键处在于sendMessage(H.BIND_SERVICE, s)
,这句代码向Handler H发送了一个BIND_SERVICE消息。H也是ActivityThread的内部类,找到这部分代码:
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
这其中执行了ActivityThread的handleBindService()
方法
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
...省略
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
...省略
- 通过
Service s = mServices.get(data.token)
获取到当前需要的Service; - 回调
IBinder binder = s.onBind(data.intent)
获取IBinder对象; - 通过
ActivityManagerNative.getDefault().publishService(
将当前的Service发布。
data.token, data.intent, binder)
这里面有2个问题,为什么mServices里面有我们需要的service?publishService又做了什么?
第一个问题涉及到Service启动流程,在这里先简单说一下,在ActivityThread中有一个handleCreateService()
方法,负责service的启动工作:
private void handleCreateService(CreateServiceData data) {
...省略
ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
...省略
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
...省略
从源码中可以发现,这里通过反射获取到service,再将该service放入mServices中,因此我们可以从mServices中获取需要的service。第一个问题解决。
ActivityManagerNative.getDefault()
在文章开头已经介绍过,是通过ServiceManager类获取系统服务,返回ActivityManagerService,我们查看其publish()
方法,发现在最后会调用ActiveServices类的publishServiceLocked()
方法
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
...省略
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 (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
try {
c.conn.connected(r.name, service);
}
...省略
是不是有点眼熟?还记得之前ActiveServices类的bindServiceLocked()
方法吗?如果当前service已经启动,就会在bindServiceLocked()
中直接通过 c.conn.connected(r.name, service)
直接完成回调。现在的情况是service还没有启动,所以我们会通过系统先启动并绑定该service,最后在publish()
方法中回调客户端的ServiceConnection类,第二个问题解决。
至此,客户端回调也完成了,整个流程结束!