Android 跨进程通信 Binder 工作流程
本篇仅讲述 Binder工作流程, 并不讲解Binder的原理
-
先说明什么是AIDL
AIDL 是 android 接口定义语言,全称 Android Interface definition language
-
AIDL和Binder作用
AIDL是基于Binder机制的,使用AIDL可以让SDK自动帮我们生成对应的Binder类。
Binder是不同进程之间通信的桥梁。 -
使用AIDL获取Binder实现类
-
使用Android Studio 创建一个 AIDL 文件
new->AIDL
-
实例名称为IBookManager.aidl
在AIDL中自定义的类型 需要使用import引入,并且自定义的类需要实现序列化接口(Serlizable或Parcelable),并使用单独的同名aidl文件声明,例如下例中 使用到的Book
package com.example.ipctest.aidl; import com.example.ipctest.aidl.Book; import com.example.ipctest.aidl.IOnNewBookArrivedListener; // Declare any non-default types here with import statements interface IBookManager { List<Book> getBookList(); //获取书籍 void addBook(in Book book); //添加书籍 } //Book.aidl package com.example.ipctest.aidl; // Declare any non-default types here with import statements parcelable Book; //Book.java public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } }
-
build一下项目 就可以在build->generated->aidl_source_output_dir->debug目录下找到自动生成的IBookManager接口
这个接口主要组成部分
- 一个默认实现的静态类 Default
- 一个静态抽象类Stub,继承Binder,实现本接口,这个就是Binder,同时它内部有一个静态内部类Proxy。可以把Stub看作服务端,Proxy看作是客户端。
- 之前在AIDL中声明的方法,以及方法对应的code值
//所有可以在binder中传输的接口都需要继承IInterface public interface IBookManager extends android.os.IInterface { /** IBookManager接口的一个默认实现类 */ public static class Default implements IBookManager { @Override public java.util.List<Book> getBookList() throws android.os.RemoteException { return null; } @Override public void addBook(Book book) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; } } //这相当于是一个binder,继承自Binder类 实现本接口 public static abstract class Stub extends android.os.Binder implements IBookManager { //binder的唯一标识符 一般用当前binder的类名标识 private static final String DESCRIPTOR = "com.example.ipctest.aidl.IBookManager"; // 默认构造函数,将Stub附加到接口上 public Stub() { this.attachInterface(this, DESCRIPTOR); } /** 静态方法 * 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端属于同一个进程 * ,那么此方法返回的就是服务端Stub对象本身,否则返回的是系统封装后的Stub.proxy对象 * 获取转换后的对象 就可以执行需要的方法 */ public static IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } //查询实现了这个Binder对象的本地实例,如果是null就说明客户端和服务端不属于同一个进程,就需要去创建一个proxy对象返回给客户端 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof IBookManager))) { return ((IBookManager)iin); } //客户端和服务端不在一个进程 使用Proxy对Binder进行包装并返回,Proxy也实现了IBookManager接口 return new IBookManager.Stub.Proxy(obj); } //返回当前binder对象 @Override public android.os.IBinder asBinder() { return this; } //这个方法运行在Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理 //调用远程服务的方法时,先执行Proxy中对应的方法,通过transact()去执行继承了Stub的类的对象的onTransact()方法,在onTransact方法中根据code值 去回调相应的接口,此时整个方法执行流程大致结束了 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0!=data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_getBookList: { data.enforceInterface(descriptor); //执行在服务端实现的getBookList()方法 java.util.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; } //执行在服务端实现的addBook()方法 this.addBook(_arg0); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } //Stub的静态内部类 Proxy 相当于客户端 private static class Proxy implements IBookManager { // 存放binder的引用,用于调用服务端的方法 private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } //getBookList()的实现 @Override public java.util.List<Book> getBookList() throws android.os.RemoteException { //data是要传递的参数,reply存放返回值 result 存放结果 android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); //先走跨进程通信的操作 看能否成功 boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_getBookList, _data, _reply, 0); //跨进程获取失败 说明 客户端和服务端在一个进程中 直接调用stub的getbooklist方法 if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBookList(); } _reply.readException(); _result = _reply.createTypedArrayList(Book.CREATOR); } finally { //回收资源 _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book!=null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } boolean _status = mRemote.transact(IBookManager.Stub.TRANSACTION_addBook, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().addBook(book); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } public static IBookManager sDefaultImpl; } //声明两个整形id用于标识自定义的方法 static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); public static boolean setDefaultImpl(IBookManager impl) { // Only one user of this interface can use this function // at a time. This is a heuristic to detect if two different // users in the same process use this function. if (Proxy.sDefaultImpl != null) { throw new IllegalStateException("setDefaultImpl() called twice"); } if (impl != null) { Proxy.sDefaultImpl = impl; return true; } return false; } public static IBookManager getDefaultImpl() { return Proxy.sDefaultImpl; } } //IBookManager接口中声明的方法 public java.util.List<Book> getBookList() throws android.os.RemoteException; public void addBook(Book book) throws android.os.RemoteException; }
-
-
工作流程
- 客户端(一个Activity)通过bindService()拿到服务端(一个Service组件)传递过来的Binder对象,并且调用IBookManager.Stub.asInterface()方法转化成客户端需要的IBookManager类型的对象(如果不在同一个进程,则使用Proxy类对Binder进行了包装并返回该对象)。通过这个对象可以调用服务端的方法
- 服务端 需要实现onbind方法,返回binder对象。这个binder对象所属的类必须继承IBookManager.Stub类,并实现IBookManager中声明的方法。(因为Stub是一个实现了IBookManager接口的抽象类,所以子类必须实现IBookManager接口声明的方法。)
- 在客户端调用服务端的一个方法,例如getBookList(),调用此方法后如果client和server不在同一个进程中就立刻调用Proxy中的相应的方法getBookList(),然后在该方法中,通过调用binder的transact()方法,转去执行Stub中的onTransaction()方法,在此方法中会调用你在服务端实现的getBookList()方法,然后依次返回结果,执行结束
注意:
- binder中的方法都是在binder线程池中执行的,所以不要在服务端的实现接口方法中又再次开启线程,除非你确定需要那样做。
- 远程服务的方法如果是耗时方法,则在客户端调用时需要开启线程,否则会ANR
参考书籍:<<Android开发艺术探索>>
文章评论