Binder:别针,回形针。生活中,我们用回形针将两张纸“别”在一起。android中,Binder用于进程间通信,即将两个进程“别”在一起。
Binder是一种架构,有3个模块(服务端接口,Binder驱动,客服端接口),如下图:
Binder服务端,实际上就是一个Binder类的对象,该对象一旦创建内部就会启动一个隐藏线程。
根据上面的架构,说说我所理解的。
客服端程序要想访问远程服务,那么它就需要获取到服务端的Binder引用---mRemote。Android工程师提供了一个解决方案,那就是Service。对于客服端来讲,可以使用两种方式来和服务端建立连接。
public ComponentaName startService(Intent intent) public boolean bindService(Intent service, ServiceConnection conn, int flags)
第一种方式启动后,客服端暂时没有服务端Binder的引用。
而第二种方式启动后,ServiceConnection接口的onServiceConnected方法会被回调。
ServiceConnection代码如下:
public interface ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service); public void onServiceDisconnected(ComponentName name); }
onServiceConnected方法中的IBinder对象service就是远程服务的引用。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我所理解的Binder机制到这里就结束了,那么AIDL的出现是为了解决什么?
编写一个aidl文件,看看它到底做了什么。如下所示:
package com.aprz.aidl; interface IAidlTest{ void test(); }
interface是关键字,有时在interface之前加上oneway表示service提供的方法都没有返回值,文件名以“I”开头是为了统一规范。文件编写完之后,android的开发环境会自动生成以下代码:
/*___Generated_by_IDEA___*/ /* * This file is auto-generated. DO NOT MODIFY. * Original file: F:\\workspace\\AIDLService\\src\\com\\aprz\\aidl\\AidlTest.aidl */ package com.aprz.aidl; public interface AidlTest extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.aprz.aidl.AidlTest { private static final java.lang.String DESCRIPTOR = "com.aprz.aidl.AidlTest"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.aprz.aidl.AidlTest interface, * generating a proxy if needed. */ public static com.aprz.aidl.AidlTest asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.aprz.aidl.AidlTest))) { return ((com.aprz.aidl.AidlTest) iin); } return new com.aprz.aidl.AidlTest.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_test: { data.enforceInterface(DESCRIPTOR); this.test(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.aprz.aidl.AidlTest { 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; } @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(); } } } static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void test() throws android.os.RemoteException; }
这些代码完成了三个任务:
1、这个文件本身是一个java interface,包含了aidl文件所声明的服务函数(文件最下面)。
2、定义了一个Proxy类,该类作为客服端访问服务端的代理,主要是为了统一包裹内写入参数的顺序(当我们在客服端使用Stub的asInterface将IBinder转换为具体的接口以便调用服务端的方法时,其实返回的Proxy对象,代理做的主要工作就是统一包裹内写入参数的顺序)。
这里需要详情解释一下这句话的意思。我们知道服务端是一个Binder对象,假设让我们来设计一个Service端,这个Service只提供两个方法:start(String filePath)和stop()。
那么其代码大致如下:
public class MusicPlayService extends Binder{ public void start(String filePath) { } public void stop(){ } //服务端必须要实现onTransact()方法 @override protected boolean onTransact(int code, Parcel data, Pacel reply, int flags) throws RemoteException { return super.onTransact(code, data, reply, flags); } }
onTransact的第二个参数data,就是客服端传递过来的参数,那么我如何正确读取这个参数呢(假设这里有多个参数)?
正确的读取方式应该是这样的:
data.enforceInterface(“MusicPlayService”); String filePath = data.readString(); start(filePath);
enforceInterface()是为了某种校验,与客服端的writeInterfaceToken()方法对应。由于我写的aidl文件并没有带参数,从它生成的代码中也无法看出什么,你可以自己做测试,给aidl的文件的方法加上多个参数,看看生成的代码中Stub和Proxy类的方法,对比一下。
3、定义一个Stub类,主要由服务端来使用。这个类之所以是抽象类,是因为具体的函数服务必须由程序员来实现,如下:
public class AIDLService extends Service { @Override public IBinder onBind(Intent intent) { return mBinder; } private final AidlTest.Stub mBinder = new AidlTest.Stub() { public void test() throws RemoteException { Log.e("aidl", "输出从服务端"); } }; }
我们需要返回一个IBinder对象,这个mBinder对象是Stub的一个实例对象,需要实现其抽象方法,Binder与IBinder的关系:public class Binder implements IBinder 。
另外,Stub中的一些常量,如TRANSACTION_test,这些常量与服务函数相对应,因为服务端收到调用消息之后,就会执行onTransact()方法,这个时候,就根据这个常量来判断,该执行哪个方法(TRANSACTION_test表示需要执行服务端的test方法)。Stub中还有一个asInterface函数,该方法返回一个Binder对象,需要注意的是,如果是从远程来获取服务端的Binder引用,会返回Binder驱动中的对象,如果是从服务端进程内部获取,则会返回服务端本身的Binder对象(当创建一个Binder对象的时候,服务端会创建一个,Binder驱动也会创建一个)。