今天在学习任主席的Android艺术开发探索时学习到了Binder,特此在本文中做一下对于Binder的总结。
首先我们为什么会使用Binder呢?
在Android系统当中,进程与进程是不可以进行直接访问的,这保证了Android进程的独立性(比如一个APP崩掉了,不会导致所有APP崩掉),同时也保证了Android进程的安全性。
但是Andorid系统进程间不可以直接进行通讯,那么我们要如何实现通讯呢?
我们可以通过Android系统底层来实现间接的进程间通讯,而这时通讯的信使,就是Binder了。通讯过程如下图
虽然在日常开发中,我们可以通过AIDL来快速的编译出一个Binder类,但是为了更好的学习我们还是要打开Binder类去分析它的?下表会列出Binder的主要组成的方法以及内部类。
DESCRIPTOR常量 | asBinder方法 | onTransact方法 | Proxy代理内部类 | asInterface方法 |
Binder如上表所述,一个继承了Binder的子类需要至少实现上面4个才可以进行进程间通信,下面来简单的描述一下这5个都具体做了哪些操作 |
---|
DESCRIPTOR
它是Binder的唯一标识符,一般会设置为包名+类名,用于指定Binder类,一般我们使用一个静态的String常量。
asInterface
用于将服务端的Binder转换为客户端所需要的AIDL接口类型的对象,这个转换是区分进程的,如果客户端和服务端位于同一个进程当中,那么就会返回服务端的Stub对象本身,反之返回Stub.proxy对象。
asBinder
这个方法返回当前的Binder对象
onTransact
该方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求的时候远程请求会通过系统底层封装后交由此方法来处理。
rotected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
从上述代码可以看出该方法有四个参数,code参数可以确定客户端具体的请求是什么,data就是去除目标方法所需要的参数如果没有就不执行,当目标代码执行完毕,就会将返回值写入在reply当中没有返回值就不会执行,如果此方法返回了false,那么客户端就会请求失败,我们可以利用这样的机制来过滤进程。
proxy
这是一个内部类,他实现了我们的接口,他创建了接口方法所需要的输入性Parcel对象,输出型Parcel对象,和返回值对象,然后把该方法的参数信息写入输入型对象里,在调用transact方法来发起RPC请求,同时线程会被挂起,服务端onTransact方法会被调用,直到RPC过程返回后,才继续执行线程,并且从输出型Parcel对象去除RPC对象返回的结果。
下面我们就手动来实现一个Binder的服务类:要做的有3部操作。(我这里使用的是任主席的例子)
1.实现继承了IInterface接口的接口。
2.在接口中实现Stub类。
3.实现继承了Binder的子类,并实现2的接口。
首先我们来实现接口并且实现Stub类。代码如下(不懂得地方可以看注释,注释写的很详细):
public interface IBookManager extends IInterface { //新建Stub类 public static abstract class Stub extends Binder implements IBookManager { List<Book> mBookList = new ArrayList<>(); private final IBookManager.Stub mBinder = new IBookManager.Stub(){ @Override //返回当前Binder public IBinder asBinder() { return mBinder; } @Override //返回书 public List<Book> getBookList() throws RemoteException { synchronized (mBookList){ return mBookList; } } //添加书 @Override public void addBook(Book book) throws RemoteException { synchronized (mBookList){ if(mBookList.contains(book)){ mBookList.add(book); } } } }; } //Binder唯一标识符 static final String DESCRIPTOR = "com.example.nixo.ipctest.manualbinder.IBookManager"; /** * 接口中要实现一个方法就要有一个方法的标识符,并且要唯一。所以这里有两个方法标识符,两个方法。 */ static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0; static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1; public List<Book> getBookList() throws RemoteException; public void addBook(Book book) throws RemoteException; }
然后我们来写Binder子类并实现上述接口:
public class BookManagerImpl extends Binder implements IBookManager { //接口连接的时候进行初始化 public BookManagerImpl() { this.attachInterface(this,DESCRIPTOR); } /** * 如果需要就会将Ibinder接口转换成IbookManager接口,并且生成proxy代理 * 这里要注意!如果是同进程就不需要proxy代理。所以在proxy那里来判断是否是同一个进程。 * @return * @throws RemoteException */ public static IBookManager asInterface(IBinder obj){ if((obj == null)){ return null; } IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if(((iin != null) &&(iin instanceof IBookManager))){ //instanceof 判断后者是否是前者的泛型 // 个人认为这里还判断了是否在同一个进程,如果为true就返回没有代理的Binder,如果false在返回代理的Binder; return ((IBookManager) iin); } return new BookManagerImpl.Proxy(obj); } @Override public IBinder asBinder() { return this; } //底层系统封装出来的一个事务处理类 // 根据不同的code也就是请求PID,处理不同的事务然后返回不同的data. @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code){ case INTERFACE_TRANSACTION:{ //验证 reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBookList:{ //跨进程获取Binder data.enforceInterface(DESCRIPTOR); List<Book> result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(result); return true; } case TRANSACTION_addBook:{ //跨进程添加 data.enforceInterface(DESCRIPTOR); Book arg0; if((0 != data.readInt())){ arg0 = Book.CREATOR.createFromParcel(data); }else{ arg0 = null; } this.addBook(arg0); reply.writeNoException(); return true; } } return super.onTransact(code,data,reply,flags); } @Override public List<Book> getBookList() throws RemoteException { //TODO 待实现 return null; } @Override public void addBook(Book book) throws RemoteException { //TODO 待实现 } private static class Proxy implements IBookManager { private IBinder mRemote; public Proxy(IBinder mRemote) { this.mRemote = mRemote; } public String getInterfaceDescriptor(){ return DESCRIPTOR; } @Override public List<Book> getBookList() throws RemoteException { //创建了输入,输出型Parcel和返回对象 Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); List<Book> result; try { //写入 data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getBookList,data,reply,0); result = reply.createTypedArrayList(Book.CREATOR); }finally { //进行回收 reply.recycle(); data.recycle(); } return result; } //add方法也是一样的,只不过不需要返回。 @Override public void addBook(Book book) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try{ data.writeInterfaceToken(DESCRIPTOR); if((book != null)){ data.writeInt(1); book.writeToParcel(data,0); }else{ data.writeInt(0); } mRemote.transact(TRANSACTION_addBook,data,reply,0); reply.readException(); }finally { reply.recycle(); data.recycle(); } } @Override public IBinder asBinder() { return mRemote; } } }
Binder |
---|
这样我们就手写好了Binder,我们可以开启一个Service来启动它达到进程间通讯的目的^_^。