1.什么是Binder
- 直观来说,Binder是Android中的一个类,它继承了IBinder接口
- 从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在linux中没有
- 从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager...)和相应ManagerService的桥梁
- 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务
这里就不对Binder不做深入的探究了,这篇文章主要是通过对AIDL分析知道AIDL是如何进行IPC的,那么让我们现在透过现象看本质。
2.AIDL的本质是什么
在 关于Service你需要知道这些 一文中有提到过在编译AIDL文件后会生成一个IxxxAIDL.java的编译文件,真正实现IPC的其实是这个文件,如果不相信你大可以在服务端和客户端只保留这个文件,一样可以实现IPC。那照这么说我们写一大堆AIDL文件只是为了生成这文件么?没错,AIDL只是在简化我们写这个.java 文件的工作而已,其实这个.java文件就是一个Binder类,为什么说是Binder类下面这个.java类的源码一摆你就会明白的。所以AIDL说到底本质就是:系统提供的一个快速实现Binder的工具而已。所以不要对AIDL有什么误会,真正实现IPC的其实是Binder。
3.透过现象看本质
我还是拿 关于Service你需要知道这些 这篇文章里生成的IMyAidl.java来说吧,下面是它的源码,为什么说这个类是一个Binder类应该明白了吧,因为继承了Binder类。其中有最重要的两个方法是transact()和onTransact()。第一个方法使你可以向远端的IBinder对象发送发出的调用,第二个方法使你的远程对象能够响应接收到的调用。
package com.example.com.test;
// Declare any non-default types here with import statements
public interface IMyAidl extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.com.test.IMyAidl {
private static final java.lang.String DESCRIPTOR = "com.example.com.test.IMyAidl";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.com.test.IMyAidl interface,
* generating a proxy if needed.
*/
public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
return ((com.example.com.test.IMyAidl) iin);
}
return new com.example.com.test.IMyAidl.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_getPerson: {
data.enforceInterface(descriptor);
String _result = this.getPerson();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
reply.writeString(_result);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.com.test.IMyAidl {
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 String getPerson() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = _reply.readString();
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public String getPerson() throws android.os.RemoteException;
}
我们都知道客户端的IMyAidl在下面的代码中被赋值的。
public void onServiceConnected(ComponentName name, IBinder service) {
mAidl = IMyAidl.Stub.asInterface(service);
}
那么我们点进去IMyAidl中看看IMyAidl.Stub.asInterface()这个方法究竟都做了什么事。
public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
//判空
if ((obj == null)) {
return null;
}
//通过DESCRIPTOR = "com.example.com.test.IMyAidl",查询在本地是否已经有可用的对象了,如果有就将其返回
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
return ((com.example.com.test.IMyAidl) iin);
}
//如果本地没有的话就新建一个返回
return new com.example.com.test.IMyAidl.Stub.Proxy(obj);
}
通过上面源码我们知道起到关键性作用的是返回的什么对象,在这里就不做深究了,只需要知道:同进程时,返回的是Stub对象,其实就是在onBind中返回的mBinder。跨进程时,返回的是Stub.Proxy对象。所以我们只需要关注Stub.Proxy这个对象干了什么。
private static class Proxy implements com.example.com.test.IMyAidl {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
//此处的 remote 就是前面的 IBinder service
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 String getPerson() throws android.os.RemoteException {
//_data用来存储客户端流向服务端的数据流,
//_reply用来存储服务端流回客户端的数据流
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//调用 transact() 方法将方法id和两个 Parcel 容器传过去
//这是一个比较重要的方法,通过这个transact()方法使你可以向远端的IBinder对象发送发出调用
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
_reply.readException();
//从_reply中取出服务端执行方法的结果
if ((0 != _reply.readInt())) {
_result = _reply.readString();
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
//将结果返回
return _result;
}
}
上面这段源码其实很好理解,这就是一个代理类,通过这个代理类生成 _data和 _reply两个数据流对象,并将传进来的参数写入到 _data中,然后mRemote这个对象也就是IBinder service这个对象通过调用transact()方法将他们传入到服务端中,并请求服务端调用相应的方法,调用这个transact()方法之后,客户端将会挂起当前线程,等候服务端执行完相关任务后通知并接收返回的 _reply 数据流,并从中取出服务端传回来的数据。
至此,关于客户端分析就是这些了。下面来看看服务端的流程
前面说了客户端通过调用 transact() 方法将数据和请求发送过去,那么理所当然的,服务端应当有一个方法来接收这些传过来的东西,这个方法就是onTransact()。
@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_getPerson: {
data.enforceInterface(descriptor);
//调用 this.getPerson() 方法,在这里开始执行具体的事务逻辑
//_result 列表为调用 getPerson() 方法的返回值
String _result = this.getPerson();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
//将_result写入 reply中
reply.writeString(_result);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
可以看到,它在接收了客户端的 transact() 方法传过来的参数后,就直接进入了一个 switch 选择:根据传进来的方法 code 不同执行不同的操作。可以看到,根据传入的TRANSACTION_getPerson直接调用服务端这边的具体方法getPerson()实现,然后获取返回值并将其写入 _reply 流,前面说到调用transact()后,客户端的线程会被挂起等待服务端执行完相关任务后通知并接收返回的 _reply。这就很好解释了为什么,客户端和服务端是通过Binder进行交互的了。
4.手撸Binder实现IPC
经过上面的分析,相信对Binder实现IPC有了一定的了解了吧。那么为了更好的理解Binder如何实现IPC,我们手撸一个Binder来实现IPC。
新建一个项目作为客户端
public class BinderActivity extends Activity {
private boolean isBound;
private int result;
private int num1;
private int num2;
private IBinder mService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isBound = true;
mService = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder);
ButterKnife.bind(this);
}
@OnClick(R.id.tv)
public void onViewClicked() {
if (isBound) {
result = getTotal(mService);
if (num1 + num2 == result) {
Toast.makeText(this, num1 + " + " + num2 + " = " + result, Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "确定你没算错?", Toast.LENGTH_SHORT).show();
}
}
}
private int getTotal(IBinder service){
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
Random random = new Random();
num1 = random.nextInt(10);
num2 = random.nextInt(10);
data.writeInt(num1);
data.writeInt(num2);
int result = 0;
try {
service.transact(1, data, reply, 0);
result = reply.readInt();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
data.recycle();
reply.recycle();
}
return result;
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent();
intent.setAction("com.example.com.test.service.ipc");
intent.setPackage("com.example.com.test");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(mConnection);
isBound = false;
}
}
}
可以看到,上面的getTotal()是不是很熟悉了,没错就是上面源码分析的客户端部分,在这里我们可以看到,我将num1和num2写入到 _data中,然后调用transact()方法,将参数传给服务端,等待服务端操作完后,再读取 _reply。
新建一个项目作为服务端
public class BinderService extends Service {
private IBinder mIBinder = new Binder(){
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == 1){
int num1 = data.readInt();
int num2 = data.readInt();
reply.writeInt(num1+num2);
return true;
}
return super.onTransact(code, data, reply, flags);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
}
可以看到在服务端的onTransact()中,根据transact()方法传入的code进行操作,读取 _data的数据经过计算将值写入 _reply中。
总结:客户端通过调用transact()将相关参数传给服务端,并将当前线程挂起,等待服务端操作。服务端根据客户端传进来的code调用服务端的具体方法实现,获取返回值写入_reply流中(服务端onTransact()其实就是读取 _data流的数据,经过一定操作写入 _reply流中),服务端操作完后,客户端的线程收到通知,读取 _reply流的数据并返回(客户端transact()其实就是将参数写入 _data流,经过一定操作读取 _reply流)。 通过一张图来帮助你理解
看到这里相信你能理解Binder实现IPC了,话不多说上结果。
运行结果: