Android 使用AIDL实现进程间通讯


文章目录

    • 1 启动远端service进程
        • 1.1 创建ServiceConnection对象
        • 1.2 绑定Service
        • 1.3 在service中创建并返回Binder
    • 2 IBookManager结构
        • 2.1 整体结构
        • 2.2 完整IBookManager类
        • 2.3 IBookManager中的抽象函数
        • 2.4 IBookManager中的内部抽象类Stub
          • 2.4.1 Stub类结构
          • 2.4.2 Stub的标识
          • 2.4.3 asInterface()方法
          • 2.4.5 onTransact()方法
        • 2.5 Stub中的静态内部类Proxy
    • 3 总结
        • 3.1 类关系图
        • 3.2 当Activity与Service不在同一进程时的日志信息
        • 3.3 当Activity与Service在同一进程时的日志信息
        • 3.4 流程总结

1 启动远端service进程 本文主要介绍如何使用AIDL来实现进程间的通讯过程。
要实现进程间通讯,首先要创建两个进程的环境。通过在AndroidMenifest中配置如下属性即可使组件运行在指定的进程中
android:process=""

例如要使Service运行在名为com.example.yeliang.testapplication:remote的进程中(com.example.yeliang.testapplication 是程序的包名),可以添加如下配置
android:name=".h_aidl.RemoteService" android:process=":remote"/>

不指定此属性的组件,则默认运行在主进程。
接下来看运行在主进程的Activity和非运行主进程的Service是如何进行通讯的。
1.1 创建ServiceConnection对象 用于作为bindService方法的入参。
ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { IBookManager bookManager = BookBinder.asInterface(binder); try { List list = bookManager.getBookList(); Log.i("=ipc=", "onServiceConnected list = " + list); } catch (RemoteException e) { e.printStackTrace(); } }@Override public void onServiceDisconnected(ComponentName name) {} };

1.2 绑定Service
Intent intent = new Intent(MainProcessActivity.this, RemoteService.class); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

此时远端进程的Service将会启动。
1.3 在service中创建并返回Binder 远端进程的Service创建后,首先在onCreate中创建用于主进程获取数据的Binder对象,然后在onBind()方法中返回此Binder.
public class RemoteService extends Service {private Binder mBinder; private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>(); @Override public void onCreate() { super.onCreate(); mBinder = new BookBinder(mBookList); mBookList.add(new Book("Java",10020)); mBookList.add(new Book("Python",10050)); }@Nullable @Override public IBinder onBind(Intent intent) { Book book = intent.getParcelableExtra("book"); Log.i("=ipc=", "RemoteService onBind 服务端返回的 binder = " + mBinder); return mBinder; }}

onBind方法返回的IBinder对象即为Activity中定义的ServiceConnection对象中的onServiceConnected方法里面的IBinder参数
onServiceConnected(ComponentName name, IBinder binder)

但是这两个IBinder并不是一个对象,本身这两个对象也不在同一个进程中。
实际上Service中onBind()方法返回的IBinder对象是BookBinder对象。
而onServiceConnected()方法中接收到的IBinder是BinderProxy对象。后续会详细介绍。
经过上面的步骤,主进程的Activity和远端进程的Service处于不同的进程。Activity中通过持有的Binder对象来调用获取远端进程的数据,进而实现进程间通讯。
2 IBookManager结构 2.1 整体结构 在的onServiceConnected()方法中,首先会通过binder来获取一个IBookManager对象
IBookManager bookManager = BookBinder.asInterface(binder);

接下来看下IBookManager接口的整体结构
Android 使用AIDL实现进程间通讯
文章图片

2.2 完整IBookManager类
public interface IBookManager extends IInterface {abstract class Stub extends android.os.Binder implements IBookManager { private static final String DESCRIPTOR = "com.example.yeliang.testapplication.h_aidl.IBookManager"; public Stub() { this.attachInterface(this, DESCRIPTOR); }//如果客户端调用asInterface,则实际传过来的binder对象是 BinderProxy 的实例 //如果服务户端调用asInterface,则实际传过来的binder对象是 IBookManager 的实例 public static IBookManager asInterface(IBinder binder) { Log.i("=ipc=", "Stub asInterface binder = " + binder); if (binder == null) { return null; }IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR); //1 以 (iInterface instanceof IBookManager)来区分当前是哪个进程 if (iInterface instanceof IBookManager) { Log.i("=ipc=", "StubsInterface iInterface instanceof IBookManager"); //2 表示同一进程直接返回 iInterface 对象 return (IBookManager) iInterface; }Log.i("=ipc=", "Stub asInterface return new IBookManager.Stub.Proxy(obj)"); //3 如果不是同一个进程 则通过binder来构造Stub.Proxy对象并返回 return new IBookManager.Stub.Proxy(binder); }@Override public IBinder asBinder() { return this; }@Override protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {Log.i("=ipc=", "Stub.onTransact code = " + code); switch (code) { case INTERFACE_TRANSACTION: Log.i("=ipc=", "Stub.onTransact case INTERFACE_TRANSACTION"); reply.writeString(DESCRIPTOR); return true; case TRANSACTION_getBookList: Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList start"); data.enforceInterface(DESCRIPTOR); List result = getBookList(); reply.writeNoException(); reply.writeTypedList(result); Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList end"); return true; case TRANSACTION_addBook: data.enforceInterface(DESCRIPTOR); Book book; if (0 != data.readInt()) { book = Book.CREATOR.createFromParcel(data); } else { book = null; } addBook(book); reply.writeNoException(); return true; }Log.i("=ipc=", "Stub.onTransact execute super"); return super.onTransact(code, data, reply, flags); }public static class Proxy implements IBookManager {private IBinder mRemote; Proxy(IBinder remote) { mRemote = remote; }public String getInterfaceDescriptor() { return DESCRIPTOR; }@Override public List getBookList() throws RemoteException { Log.i("=ipc=", "Stub.Proxy getBookList" + ", binder = " + mRemote); Parcel data = https://www.it610.com/article/Parcel.obtain(); Parcel reply = Parcel.obtain(); List bookList; try { data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0); Log.i("=ipc=", "Stub.Proxy transact done"); reply.readException(); bookList = reply.createTypedArrayList(Book.CREATOR); } finally { reply.recycle(); data.recycle(); }return bookList; }@Override public void addBook(Book book) throws RemoteException { Parcel data = https://www.it610.com/article/Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(DESCRIPTOR); if (book != null) { data.writeInt(1); book.writeToParcel(data, 0); } else { data.writeInt(0); }try { mRemote.transact(Stub.TRANSACTION_addBook, data, reply, 0); reply.readException(); } finally { reply.recycle(); data.recycle(); } }@Override public IBinder asBinder() { return mRemote; } }static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0; static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1; }List getBookList() throws RemoteException; void addBook(Book book) throws RemoteException; }

IBookManager是一个接口类,并且继承自IInterface对象
public interface IBookManager extends IInterface

IInterface是Binder的基础类,使用Binder来作为进程间通讯则必须实现此接口。
2.3 IBookManager中的抽象函数
List getBookList() throws RemoteException; void addBook(Book book) throws RemoteException;

【Android 使用AIDL实现进程间通讯】当客户端持有IBookManager对象的实例时,即可通过此接口函数来获取远端进程的数据。
2.4 IBookManager中的内部抽象类Stub 2.4.1 Stub类结构 Stub首先继承自Binder,并且实现了IBookManager接口。
abstract class Stub extends android.os.Binder implements IBookManager

2.4.2 Stub的标识 再来看Stub中,首先是用来标识Stub的DESCRIPTOR。
private static final String DESCRIPTOR = "com.example.yeliang.testapplication.h_aidl.IBookManager";

2.4.3 asInterface()方法 此方法会根据binder来创创建并返回一个IBookManager实例。
public static IBookManager asInterface(IBinder binder) { Log.i("=ipc=", "Stub asInterface binder = " + binder); if (binder == null) { return null; }IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR); //1 以 (iInterface instanceof IBookManager)来区分当前是哪个进程 if (iInterface instanceof IBookManager) { Log.i("=ipc=", "StubsInterface iInterface instanceof IBookManager"); //2 表示同一进程直接返回 iInterface 对象 return (IBookManager) iInterface; }Log.i("=ipc=", "Stub asInterface return new IBookManager.Stub.Proxy(obj)"); //3 如果不是同一个进程 则通过binder来构造Stub.Proxy对象并返回 return new IBookManager.Stub.Proxy(binder); }

入参binder:
如果客户端(统一进程)调用asInterface,则实际传过来的binder对象是 BinderProxy 的实例。
如果服务户端调用asInterface,则实际传过来的binder对象是 IBookManager 的实例即BookBinder对象。
返回值IBookManger
如果调用asInterface()方法入参为BinderProxy的实例,即需要跨进程传输数据,则返回IBookManger.Stub.Proxy实例。
如果是同一进程调用asInterface()方法,则返回的是BookBinder实例。
2.4.5 onTransact()方法 此方法运行在服务端的进程中,用于把服务端的数据写入到Parcel对象中,然后返回给客户端。
@Override protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {Log.i("=ipc=", "Stub.onTransact code = " + code); switch (code) { case INTERFACE_TRANSACTION: Log.i("=ipc=", "Stub.onTransact case INTERFACE_TRANSACTION"); reply.writeString(DESCRIPTOR); return true; case TRANSACTION_getBookList: Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList start"); data.enforceInterface(DESCRIPTOR); List result = getBookList(); reply.writeNoException(); reply.writeTypedList(result); Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList end"); return true; }Log.i("=ipc=", "Stub.onTransact execute super"); return super.onTransact(code, data, reply, flags); }

当主进程通过IBookManager的实例即IBookManager.Stub.Proxy对象调用getBookList()方法时,Proxy将会通过transact()函数来发起远端进程的转换。
mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0);

紧接着将会调用Stub中的onTransact()函数,onTransact()函数将会调用BookBinder获取响应数据,并将此数据写入到Pacel对象中。此时客户端即可获取到服务端的数据。
2.5 Stub中的静态内部类Proxy IBookBinder.Stub.Proxy是客户端的一个代理对象,通过此对象可以获取到服务端即对端进程的数据。Proxy实现了IBookManager的接口,但是这些接口并不真正做远端数据的写入操作,而是起着转发的作用,即将所要获取的数据转发给服务端的Stub#onTransact()方法执行。
3 总结 3.1 类关系图 Android 使用AIDL实现进程间通讯
文章图片

3.2 当Activity与Service不在同一进程时的日志信息
2020-07-11 15:20:14.274 15752-15752/? I/=ipc=: RemoteService onBind 服务端返回的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@c1d8a61 2020-07-11 15:20:14.275 15752-15780/? I/=ipc=: Stub.onTransact code = 1079135572 2020-07-11 15:20:14.275 15752-15780/? I/=ipc=: Stub.onTransact execute super 2020-07-11 15:20:14.275 15267-15267/com.example.yeliang.testapplication I/=ipc=: Activity onServiceConnected 客户端接收到的 binder = android.os.BinderProxy@eb7fe88 2020-07-11 15:20:14.275 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub asInterface binder = android.os.BinderProxy@eb7fe88 2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub asInterface return new IBookManager.Stub.Proxy(obj) 2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub.Proxy getBookList, binder = android.os.BinderProxy@eb7fe88 2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact code = 1 2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact case TRANSACTION_getBookList start 2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: BookBinder getBookList, binder = com.example.yeliang.testapplication.h_aidl.BookBinder@c1d8a61 2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact case TRANSACTION_getBookList end 2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub.Proxy transact done 2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: onServiceConnected list = [Book{bookId=10020, bookName='Java'}, Book{bookId=10050, bookName='Python'}]

3.3 当Activity与Service在同一进程时的日志信息
2020-07-11 15:22:37.936 16156-16156/com.example.yeliang.testapplication I/=ipc=: RemoteService onBind 服务端返回的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021 2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: Activity onServiceConnected 客户端接收到的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021 2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: Stub asInterface binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021 2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: StubsInterface iInterface instanceof IBookManager 2020-07-11 15:22:37.938 16156-16156/com.example.yeliang.testapplication I/=ipc=: BookBinder getBookList, binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021 2020-07-11 15:22:37.938 16156-16156/com.example.yeliang.testapplication I/=ipc=: onServiceConnected list = [Book{bookId=10020, bookName='Java'}, Book{bookId=10050, bookName='Python'}]

3.4 流程总结 1 首先定义IBookManager接口,此接口中定义了需要获取远端数据的方法。并且IBookManager实现了IInterface接口,表明IBookManager支持跨进程传输数据。
2 主进程需要获取远端进程的数据时,首先通过onServiceConnected(ComponentName name, IBinder binder)方法返回的IBinder对象来构建BookManager的实例
IBookManager bookManager = BookBinder.asInterface(binder);

因为IBookManager本身已经定义了远端进程获取数据的接口,而此处又获取到了IBookManger的实例。所以即可通过此实例来获取到远端的数据。
3 当Service和Activity运行在不同的进程时,Activity接收到的是binder为BinderProxy的实例。
当Service和Activity运行在同一的进程时,Activity接收到的binder即为BookBinder的实例。
4 BookBinder继承自BookManager.Stub类,所以BookBinder.asInterface()会调用到IBookManage.Stub的asInterface()方法。在此方法中会根据binder对象的不同返回不同的IBookManager实例。
5 当binder对象为BinderProxy实例时,会根据此binder创建一个BookManger.Stub.Proxy对象。
6 当主进程通过BookManger.Stub.Proxy获取远端方法数据时,首先会调用transact()方法
mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0);

7 然后会调用BookManager.Stub类中的onTransat()方法,并将获取到数据写入到reply中
data.enforceInterface(DESCRIPTOR); List result = getBookList(); reply.writeNoException(); reply.writeTypedList(result);

8 此时IBookManager.Stub.Proxy中已经能够获取到返回的reply值
bookList = reply.createTypedArrayList(Book.CREATOR);

    推荐阅读