参考资料
- Android Binder详解 https://mr-cao.gitbooks.io/android/content/android-binder.html
简介
在初学Android的时候,一般是从四大组件开始学起的。最开始学,是通过追代码流程的方式快速熟悉Android系统框架。
在这个速学的过程中,很多细节部分就被忽略掉了(这也是必须的,否则学起来极其痛苦且缓慢)。
比如四大组件之一的Service,在跨进程通信的时候,我们只知道是通过Binder通信的。至于内部实现是如何就不甚了了。
接下来我们通过一个简单的Demo深入探究这个跨进程通信的过程。
一. 在App之间使用Binder通信
目标是在AppClient中给AppServer发送一个简单的字符串
1.1 创建Interface
首先在服务端提供接口文件:IDemoInterface.
Android Studio中通过"File->New->AIDL"创建这个 aidl 文件。
interface IDemoInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void sayHello(long aLong, String aString);
}
创建之后需要在 “Build->Rebuild Project”, 生成对应可用的文件
1.2 实现服务端接口
class DemoService : Service() {
val mName = "DemoService"
class DemoBinder:IDemoInterface.Stub() {
override fun sayHello(aLong: Long, aString: String?) {
Log.d("DemoService", "$aString:$aLong")
}
}
private val binder = DemoBinder()
override fun onBind(intent: Intent?): IBinder? {
return binder
}
}
很简单,就是将传入的字符串输出出来。但是这个地方就有一个疑问,为啥是继承IDemoInterface.Stub呢?直接看这个文件的内容:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.oneplus.opbench.server;
// Declare any non-default types here with import statements
public interface IDemoInterface extends android.os.IInterface
{
/** Default implementation for IDemoInterface. */
public static class Default implements com.oneplus.opbench.server.IDemoInterface
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void sayHello(long aLong, java.lang.String aString) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.oneplus.opbench.server.IDemoInterface
{
private static final java.lang.String DESCRIPTOR = "com.oneplus.opbench.server.IDemoInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.oneplus.opbench.server.IDemoInterface interface,
* generating a proxy if needed.
*/
public static com.oneplus.opbench.server.IDemoInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.oneplus.opbench.server.IDemoInterface))) {
return ((com.oneplus.opbench.server.IDemoInterface)iin);
}
return new com.oneplus.opbench.server.IDemoInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_sayHello:
{
data.enforceInterface(descriptor);
long _arg0;
_arg0 = data.readLong();
java.lang.String _arg1;
_arg1 = data.readString();
this.sayHello(_arg0, _arg1);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.oneplus.opbench.server.IDemoInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void sayHello(long aLong, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(aLong);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().sayHello(aLong, aString);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.oneplus.opbench.server.IDemoInterface sDefaultImpl;
}
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.oneplus.opbench.server.IDemoInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.oneplus.opbench.server.IDemoInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void sayHello(long aLong, java.lang.String aString) throws android.os.RemoteException;
}
注释的就很清楚,是自动生成的文件。Stub是一个静态内部抽象类,继承了Binder和接口IDemoInterface。
1.3 客户端调用
注意把服务端aidl文件夹内容拷贝到客户端项目的aidl文件内,然后客户端也rebuild一下project.
/*
<!-- for binder(aidl) -->
<service android:name=".server.DemoService"
android:process=".DemoService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.DemoService"/>
</intent-filter>
</service>
*/
private fun attemptToBindService() {
val intent = Intent()
Log.e("client", " connected now")
intent.action = "android.intent.action.DemoService" //服务类的Action
intent.`package` = "com.oneplus.opbench" //服务端包名
// 建立通信
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
private val mDemoServiceConnection = object : ServiceConnection {
override fun onBindingDied(name: ComponentName?) {
Log.d("Client", "DemoService died!")
}
override fun onServiceDisconnected(p0: ComponentName?) {
Log.d("Client", "DemoService disconnected!")
}
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
Log.d("Client", "DemoService connected")
// 远程服务连接成功,打个招呼
val mProxyBinder = IDemoInterface.Stub.asInterface(p1)
try {
mProxyBinder.sayHello(5000, "Hello?")
} catch (e:RemoteException) {
}
}
}
相关类图
接下来我们从客户端的bindService建立远程连接开始看看是怎么通信的,当然这里我们重点是binder通信,而不是service的bind流程。
二. 通信过程
分析跨进程通信的过程,一定要时刻谨记当前代码所处的进程哦, 为了方便和聚焦, 忽略非紧要代码。
2.1 Client=>ContextImpl.bindService
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
getUser());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
// ......
if (mPackageInfo != null) {
if (executor != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
// 2.1.1 获取IServiceConnection对象
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
}
// .......
// 2.2 看来是先与Android系统中的AMS服务通信
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
// ......
}
2.1.1 LoadedApk.getServiceDispatcher
@UnsupportedAppUsage
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
return getServiceDispatcherCommon(c, context, handler, null, flags);
}
private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
Context context, Handler handler, Executor executor, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
sd = map.get(c);
}
if (sd == null) {
if (executor != null) {
sd = new ServiceDispatcher(c, context, executor, flags);
} else {
// 第一次建立连接,当然需要新建
sd = new ServiceDispatcher(c, context, handler, flags);
}
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler, executor);
}
// 返回的是ServiceDispatcher里的mIServiceConnection对象,是InnerConnection
return sd.getIServiceConnection();
}
}
这里就是将ServiceConnection做一个打包,存起来,隐藏细节,注意到返回的是 InnerConnection 类型哦。
相关类图如下:
2.2 SytemServer=>AMS.bindIsolatedService
这里其实就存在Binder通信,但是我们先不看,假设直接call过来了(四大组件之Service).
我们知道,当服务端app进程没有启动时,会先将进程启动,然后继续进行bindService操作,为了方便,这里假设服务端进程已启动。
这里忽略中间一系列调用,走到ActiveServices.bindServiceLocked中:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
// ......
// 注意这里传入的connection是IServiceConnection
// retrieveServiceLocked是从已安装的package中找到对应包以及指定Service组件
ServiceLookupResult res =
retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
callerFg, isBindExternal, allowInstant);
// ......
ServiceRecord s = res.record;
// ......
// 根据我们之前的研究过的Service知识,这个AppBindRecord就是记录App之间Service通信的
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
callerApp.uid, callerApp.processName, callingPackage);
// 注意这里的connection.asBinder调用的就是IServiceConnection.Stub.Proxy.asBinder
// 返回的就是mRemote
IBinder binder = connection.asBinder();
// ......
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
// c.conn就是Client端传入的IServiceConnection
// 所以这里又调回去Client进程了,注意传入的b.intent.binder对象
// 这个传入的b.intent.binder对象就是服务端App发布Service时传入的(publishServiceLocked)
// 其实可以猜得到是这个就是对应服务端App的IDemoInterface对象
// 2.3 转到Client进程
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortInstanceName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
}
2.3 Client=>LoadedApk.ServiceDispatcher.InnerConnection.connected
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service, dead);
}
}
2.3.1 LoadedApk.ServiceDispatcher.connected
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
// 2.3.2 这里其实最后还是调用到了doConnected
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
2.3.2 LoadedApk.ServiceDispatcher.doConnected
public void doConnected(ComponentName name, IBinder service, boolean dead) {
// ......
// 这个service就是服务端App中的DemoBinder中的mRemote了
if (service != null) {
// mDemoServiceConnection通知已经建立连接
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
mConnection.onNullBinding(name);
}
}
2.3.3 客户端的ServiceConnection
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
Log.d("Client", "DemoService connected")
// 远程服务连接成功,打个招呼
val mProxyBinder = IDemoInterface.Stub.asInterface(p1)
try {
mProxyBinder.sayHello(5000, "Hello?")
} catch (e:RemoteException) {
}
}
到这里,Android系统中两个进程通过四大组件之一的Service进行跨进程通信的连接已经建立了。
简单来说这个过程借助了SystemServer的帮助:Client app <–> SystemServer <–> Server app
Server App将Service的IBinder保存在SystemServer中,在Client App通过bindService的时候,传入。这样Client App就有了和Server App通信的基础。
搞清楚是怎么建立连接的过程,接下来深入探究下mProxyBinder.sayHello调用到不同进程对应的方法的细节。