Binder是Android系统进程间通信(IPC)方式之一,他的使用方法如下
先定义一个aidl文件
package com.example.binderdemo;
interface IBinderTest {
void setName(String name);
String getName();
}
接着我们写一个service,里面实现具体的功能
public class BinderService extends Service {
private String name;
public BinderService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinderTest;
}
IBinderTest.Stub mBinderTest = new IBinderTest.Stub() {
@Override
public void setName(String name) throws RemoteException {
Log.e("xw", "BinderService---setName---" + name);
BinderService.this.name = name;
}
@Override
public String getName() throws RemoteException {
Log.e("xw", "BinderService---getName---" + name);
return name;
}
};
}
再写一个应用跨进程太费劲,我们就在一个应用中来模拟跨进程调用,要使我们的service运行在另一个进程也简单,在manifest的对应service中添加process一项
<service
android:name=".BinderService"
android:process=":test" />
试验下,在activity中开启service
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, BinderService.class));
}
运行应用,在ddms中可以看到的确开启了两个进程
接着我们利用bindservice来进行跨进程调用service中的方法
public class MainActivity extends AppCompatActivity {
IBinderTest mTestBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BinderService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
initButton();
}
private void initButton() {
findViewById(R.id.bt_set).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
mTestBinder.setName("xw");
Log.e("xw", "MainActivity---setName---xw");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.bt_get).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.e("xw", "MainActivity---getName---" + mTestBinder.getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private ServiceConnection conn = new ServiceConnection() {
/** 获取服务对象时的操作 */
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mTestBinder = IBinderTest.Stub.asInterface(service);
}
/** 无法获取到服务对象时的操作 */
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
};
}
很简单,acticity中有两个按钮,一个设置,一个获取
点击setName按钮时log信息
点击getName按钮时log信息
可以看到,的确实现了跨进程调用的功能,那么接下来开始分析到底binder是如何工作的
首先我们应该了解aidl并不是必须的,它的功能就是快速的生成用来进程间通信的文件,也就是下面
package com.example.binderdemo;
public interface IBinderTest extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IBinderTest{
......
private static class Proxy implements com.example.binderdemo.IBinderTest
{
......
}
}
......
}
代码很多,但是他们都是分属于上面的一个接口,两个类,我们分别抽出来看,首先是IBinderTest
public interface IBinderTest extends android.os.IInterface {
public void setName(java.lang.String name) throws android.os.RemoteException;
public java.lang.String getName() throws android.os.RemoteException;
}
其实就是继承了IInterface 的一个接口,里面有两个等待实现的方法,也就是我们在aidl文件中定义的方法,接着看Stub
public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IBinderTest
{
private static final java.lang.String DESCRIPTOR =
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.binderdemo.IBinderTest asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.binderdemo.IBinderTest))) {
return ((com.example.binderdemo.IBinderTest)iin);
}
return new com.example.binderdemo.IBinderTest.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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
一个继承了binder实现了IBinderTest接口的抽象类,他的几个方法要着重介绍下
asInterface(android.os.IBinder obj):他是在Client端调用,用以返回一个IBinderTest对象。而且根据传入的参数,他会返回两类对象,一个就是我们实现的具体功能的类,一类则是下面要讲的第三个类Proxy。关于按什么规则返回等下再说
onTransact:我们利用binder进行进程间通信时,其实我们并没有获取到Server端的binder对象,我们获取到的只是binder驱动里与Server端的binder对象对应的一个binder代理对象。真正调用的时候binder驱动就像有一个密码箱,我们将我们需要传递的数据以及调用方法的信息放进密码箱后由binder驱动交给Server端的binder对象,因为binder类是由自己定义的,所以我们当然可以打开密码箱,取出所需要的信息。onTransact相当于打开密码箱的过程,而与之相对应的加密过程则在Proxy类中
最后看看Proxy类
private static class Proxy implements com.example.binderdemo.IBinderTest
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
......
@Override public void setName(java.lang.String name) 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.writeString(name);
mRemote.transact(TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
在setName和getName中的
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
就是将我们的数据按照指定顺序放入
mRemote.transact(TRANSACTION_getName, _data, _reply, 0);
mRemote.transact(TRANSACTION_setName, _data, _reply, 0);
transact的第一个参数告诉Server端的binder应该调用哪个方法
总结说来,IBinderTest 是定义需要实现哪些方法的接口;Stub在Server端和client端都有作用,在Server端负责实现具体业务以及接收包裹,调用指定的方法,在client端则是用asInterface来返回指定的IBinderTest对象;Proxy则是在client端产生作用,负责打包,并将包裹扔给binder驱动
分析了以上三个的各自的功能,那么接下来看看到底他们是如何工作的
来到Client端,再看看代码
bindService(intent, conn, BIND_AUTO_CREATE);
private ServiceConnection conn = new ServiceConnection() {
/** 获取服务对象时的操作 */
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mTestBinder = IBinderTest.Stub.asInterface(service);
}
/** 无法获取到服务对象时的操作 */
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
};
bindService完成后,系统会回调ServiceConnection的onServiceConnected方法,然后传给我们的参数中第二个参数为IBinder service,我们也是通过将这个参数转换为我们需要的binder对象来调用Server端binder的方法,那么这个IBinder service我们想当然的认为就是我们需要的实现了具体逻辑的binder对象,其实并不是的,我们来看看他到底是个什么玩意
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.e("MainActivity", service.getClass().getName());
mTestBinder = IBinderTest.Stub.asInterface(service);
}
看看log
这是个BinderProxy类,瞅瞅他的代码
final class BinderProxy implements IBinder {
public native boolean pingBinder();
public native boolean isBinderAlive();
public IInterface queryLocalInterface(String descriptor) {
return null;
}
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
return transactNative(code, data, reply, flags);
}
public native String getInterfaceDescriptor() throws RemoteException;
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
public native void linkToDeath(DeathRecipient recipient, int flags)
throws RemoteException;
public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
......
}
很好,一堆native方法,基本是不懂的,不过还好有一个queryLocalInterface方法,我们就需要这个,接着回去看我们的ServiceConnection
IBinderTest mTestBinder = IBinderTest.Stub.asInterface(service);
回顾下这个静态方法做了什么
public static com.example.binderdemo.IBinderTest asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.binderdemo.IBinderTest))) {
return ((com.example.binderdemo.IBinderTest)iin);
}
return new com.example.binderdemo.IBinderTest.Stub.Proxy(obj);
}
在里面调用了IBinder obj的queryLocalInterface方法,我们的obj是BinderProxy类,往上翻翻看BinderProxy的queryLocalInterface返回了什么,是个null,所以asInterface方法会返回一个Proxy类,而那个IBinder obj作为了Proxy的一个构造参数。
那么现在我们Client端持有了一个Proxy对象,对象里面存储着binder驱动传递给我们的一个BinderProxy对象,而且这个BinderProxy对象在binder驱动中肯定和Server端的binder有着对应关系
最后我们看看调用方法走的什么流程,比如说设置名字
mTestBinder.setName("xw");
mTestBinder是一个Proxy
@Override public void setName(java.lang.String name) 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.writeString(name);
mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
就是将我们的数据打包,然后调用mRemote的transact方法
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
mRemote就是binder驱动给我们的BinderProxy对象,看看BinderProxy的transact方法
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
return transactNative(code, data, reply, flags);
}
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
很忧伤。。。又调到了native方法里,不过我们也可以猜测,之后应该是binder驱动做了一些工作,然后将我们的参数传递给Server端,调用binder对象的onTransact方法
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
然后Server端的binder会根据code值去选择具体的处理方法,实现了跨进程调用的功能