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