Binder 是什么? 顾名思义 Binder 英文意思 别针,回形针,在android中 主要用于进程间通信。android 中Binder 工作于Linux层面,属于一个驱动,只是该驱动不需要硬件,或者说其操作的硬件是基于一小段内存。从线程角度来讲,Binder 驱动代码运行在内核端,客户端程序调用Binder是通过系统调用完成的。
Binder 跨进程通信是如何完成的?
在Binder 主要提供了三个模块,即 服务端接口,Binder驱动,客户端接口 三者的关系可以看下图
下面我们来详细介绍Binder中 这三个模块的主要功能:
1:服务端:一个Binder 服务端其实就是一个Binder类对象,服务端内该Binder对象一旦创建,就会启动一个隐藏线程,而该线程的作用是和Binder驱动 进行消息的交互。当该隐藏线程收到消息之后就会执行Binder对象中的onTransact() 方法,根据该方法的传入参数执行不同的服务代码。
2:Binder驱动 : Binder驱动是客户端雨服务端进行通信的桥 ,Binder驱动中的mRemote对象实质上也是一个Binder对象,该对象是在服务端创建Binder对象时,同时创建的。而客户端要访问远程服务端时,都是通过Binder驱动中的mRemote对象进行访问的。而在Binder驱动中同样重载类transact() 方法
重载transact() 方法内容主要包括:
2.1:向服务端隐藏线程发送调用消息
2.2:挂起当前线程,等待服务端执行完成之后通知
2.3:接到服务端通知后,继续执行客户端线程,并返回执行结果
3:客户端:客户端想要访问远程服务,必须获取远程服务在Binder 对象中对应的mRemote对象。
注:对于应用程序开发来说,客户端似乎是直接调用了服务端Binder对象,而实质上是客户端调用了Binder驱动中的Binder对象,然后通过Binder驱动中的Binder对象与服务端的Binder进行通信,所以应用程序中是存在两个Binder对象,一个服务端Binder,一个Binder驱动中的Binder。
下面我们从两个方面对Binder 进行详细的分析,分别是客户端通过Binder调用系统服、自定义一个远程服务客户端通过Binder进行调用
1:客户端通过Binder调用系统服务;
在调用系统服务时我们通常使用 getSystemService(String str);方法去获取系统服务;
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE)
inputManager.getInputDeviceIds();
下面我们从这段代码开始分析看客户端是如何获取到服务端Binder对象,以及通过该Binder对象客户端如何与服务端进行通信的。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback{
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
...
return super.getSystemService(name);
}
}
通过上述代码我们可以看到实质上是调用了ContextThemeWrapper.getSystemService(name);方法;
public class ContextThemeWrapper extends ContextWrapper {
@Override
public Object getSystemService(String name) {
...
return getBaseContext().getSystemService(name);
}
}
而在ContextThemeWrapper中我们可以看出实际上是调用了ContextImpl中的getSystemService(name);方法
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
在该段代码中可以看出实质上是调用到了SystemServiceRegistry 类中的getSystemService(…)方法,
private static final HashMap> SYSTEM_SERVICE_FETCHERS =
new HashMap>();
static abstract interface ServiceFetcher {
T getService(ContextImpl ctx);
}
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
可以看出在SystemServiceRegistry 类内部通过Map将name与 ServiceFetcher 一一对应进行存放,然后通过Map的key - value 值先获取ServiceFetcher 然后通过getService()获取Object对象,而getService()方法实质上是 ServiceFetcher 抽象类中的一个抽象方法。
而ServiceFetcher抽象方法的具体实现是:
static abstract class CachedServiceFetcher implements ServiceFetcher {
...
}
static abstract class StaticServiceFetcher implements ServiceFetcher {
...
}
static abstract class StaticApplicationContextServiceFetcher implements ServiceFetcher {
...
}
通过上述源码可以看出 getService(…)方法实质上是 执行了
CachedServiceFetcher 中的getService方法。
private T mCachedInstance;
@Override
public final T getService(ContextImpl unused) {
synchronized (StaticServiceFetcher.this) {
if (mCachedInstance == null) {
mCachedInstance = createService();
}
return mCachedInstance;
}
}
public abstract T createService();
可以看出在改方法中Object 对象是通过数组获取的,如果数组中没有该对象,则会创建一个Object对象,然后将该对象存放到数组中。
而在SystemServiceRegistry 类中我们可以看到内部有一个static 代码块在这段代码块中我们可以看到;
static{
...
registerService(Context.INPUT_SERVICE, InputManager.class,
new StaticServiceFetcher() {
@Override
public InputManager createService() {
return InputManager.getInstance();
}});
....
registerService(Context.LOCATION_SERVICE, LocationManager.class,
new CachedServiceFetcher() {
@Override
public LocationManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
}
内部执行了一些列的注册活动,
private static final HashMap, String> SYSTEM_SERVICE_NAMES =
new HashMap, String>();
private static final HashMap> SYSTEM_SERVICE_FETCHERS =
new HashMap>();
private static void registerService(String serviceName, Class serviceClass,
ServiceFetcher serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
在注册方法中实质上是将’ key-value’ 数据存储到Map容器中;
下面我们单独看 INPUT_SERVICE 是如何存储到,在 registerService 过程中我们只需要关注
new StaticServiceFetcher() {
@Override
public InputManager createService() {
return InputManager.getInstance();
}}
而上述代码实质上是返回了一个InputManager对象,而在InputManager类中
public static InputManager getInstance() {
synchronized (InputManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
sInstance = new InputManager(IInputManager.Stub.asInterface(b));
}
return sInstance;
}
}
我们可以看到 内部首先 通过ServiceManager.getService(…)方法获取一个IBinder对象,而改IBinder对象实质上是其他系统Service的Binder引用,
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
然后通过 new 出一个InputManager 出入到参数为 IInputManager.Stub.asInterface(b),该参数实质上是一个IInputMethodManager的统一接口。
private final IInputManager mIm;
private InputManager(IInputManager im) {
mIm = im;
}
在我们调用InputManager内部方法是可以看到实质上是调用了IInputManager中的方法
public InputDevice getInputDevice(int id) {
synchronized (mInputDevicesLock) {
...
InputDevice inputDevice = mInputDevices.valueAt(index);
...
return inputDevice;
}
}
通过上述代码的调用我们可以看到实质上是 通过InputManager中的IBinder对象 获取了远程的IInputManager接口对象,调用了远程的方法。
至此 我们可以看出系统服务中是如何通过Binder进行远程调用的。
总结:系统的各种服务的获取实质上 是先通过ServiceManager对象获取到不同Manager对应的IBinder对想,然后通过IBinder对象去获取远程接口,然后通过该远程接口调用远程方法去实现客户端的需求。
下面我们以图示的方式说明系统内部是如何跨进程调用的:
----------------------------
2:下面我们来看一下如何通过Binder来自定义跨进程通信:
关于自定义跨进程通信 android提供了统一的方法--aidl,下面我们就以aidl方式来讲解通过Binder 如何实现的跨进程通信。
先来看一段aidl如何实现跨进程通信的实例:
1:定义aidl
interface IKernelInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void test();
}
2:实现一个Service 并在该Service中实现aidl中自定义的方法,并将Binder对象通过 onBind 方法返回;
public class KernelService extends Service {
private IBinder iBinder = new IKernelInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void test() throws RemoteException {
Log.d("TAG","aidl");
}
}.asBinder();
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
}
3:客户端 通bindService启动Service 并实现ServiceConnection接口,在该接口中获取其它进程中的接口,然后通过该接口调用内部方法。
Intent intent = new Intent(this,KernelService.class);
bindService(intent,new KernelConnection(), Context.BIND_AUTO_CREATE);
class KernelConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IKernelInterface iKernelInterface = IKernelInterface.Stub.asInterface(service);
try {
iKernelInterface.test();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
以上就是aidl的实现步骤,通过以上步骤我们可以实现跨进程通信。
下面我们通过源码来分析上述代码是如何实现跨进程通信的。
在文章的开始我们提到跨进程通信实质上是客户端获取到Binder驱动中的Binder对象,然后调用 transact(…)方法实现跨进程通信的。
在aidl生成的文件中我们可以看到 Binder驱动中transact(…) 方法实质上系统已经帮我们实现了。在我们调用test() 方法是内部实质上已经调用到了该方法
@Override
public void test() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain()
android.os.Parcel _reply = android.os.Parcel.obtain()
try {
_data.writeInterfaceToken(DESCRIPTOR)
mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0)
_reply.readException()
} finally {
_reply.recycle()
_data.recycle()
}
}
所以我们并不需要在aidl中关注如何 获取到mRemote对象,并调用了transact 方法,因为这些系统已经帮我们实现了,我们现在所关注的是 客户端如何去调用test方法。
按照我们的编程思想,我们只需要在某个类中实现这个接口,并在客户端调用该接口中的方法即可。
实际上aidl中的实现也是这样实现的,首先在Service中 定义一个IBinder对象,而该对象实质上实现是
public static abstract class Stub extends android.os.Binder implements IKernelInterface
并在Service中实现 IKernelInterface接口中的方法。
到这里很多读者认为在Service中已经实现了该接口方法,客户端只需要获取到Service对象的实例,然后调用 IKernelInterface 中的方法不就可以了吗?
针对这个问题 我们可能忽略了一个问题,就是 Service很可能不在我们当前这个进程,既然不在我们当前的进程,我们又如何去获取该对象的实例呢。
针对这个问题 android 系统为我们提供了很好的方法。
因为我们接口的实现是通过Service实现的,android系统为我们提供了 bindService方法来获取到远程到Binder对象,然后通过该Binder对象,我们可以得到 我们需要的接口
在aidl文件中我们可以看到该方法
public static com.liuluchao.kernelaidl.IKernelInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
if (((iin != null) && (iin instanceof com.liuluchao.kernelaidl.IKernelInterface))) {
return ((com.liuluchao.kernelaidl.IKernelInterface) iin)
}
return new com.liuluchao.kernelaidl.IKernelInterface.Stub.Proxy(obj)
}
通过asInterface 方法我们可以得到 IKernelInterface 接口,得到该接口之后,我们就可以在客户端调用接口中的方法,这样就实现了跨进程通信。
在上述过程中还存在一个问题就是,我们如何在客户端获取到 远程服务为我们提供的Binde对象?
针对这个问题我们可以通过bindService方法的源码进行分析:
bindService在执行过程中实质上是执行了ContextImpl.bindService(…)方法
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
可以看出内部直接调用了bindServiceCommon方法 ,
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
....
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
...
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
}
在该方法中我们只需要关注上述代码即可, 通过上述代码 我们看到实质上是去执行了 ActivityManagerService中的bindService方法
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);
}
}
其内部实质上是调用了ActiveServices.bindServiceLocked(…) 方法
final IServiceConnection conn;
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException{
....
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
...
c.conn.connected(s.name, b.intent.binder);
...
}
这段代码很长我们只看我们需要关注的点。
1:通过外部传入的Service 获取到AppBindRecord 对象。
通过AppBindRecord.intent 获取IntentBindRecord 对象;在IntentBindRecord 对象中获取IBinder对象
final class IntentBindRecord {
...
IBinder binder;
}
这样就获取到了外部Service中的IBinder对象;
2:c.conn 该对象为 IServiceConnection 而IServiceConnection接口中有几个抽象方法
private static class InnerConnection extends IServiceConnection.Stub {
....
public void connected(ComponentName name, IBinder service) throws RemoteException {
...
}
}
通过上述代码我们可以看出 在 ActiveServices的 bindServiceLocked 方法中将IBinder对象回调到了外部。
而Service的onBind方法在Service启动时会执行该方法,并将改方法 得到的IBinder对象保存到 IntentBindRecord 中,具体到详细执行过程 读者可以看
该文章就可以了解。
至此我们 通过aidl实现进程间通信,读者应该有了更深一步的了解。
下面是视图对aidl进程间进一步说明:
ok 至此进程间的通信已经讲解完了,内容中如存在不正确的地方,还请各位看官指出来。