文章目录
- 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接口的整体结构
文章图片
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 类关系图
文章图片
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);
推荐阅读
- 多渠道|Android 组件化在公用Module里实现多渠道打包配置
- Android基础|EventBus源码分析之订阅-发布模型
- Android基础|Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)
- HandlerThread(子线程也可以有消息传递机制)
- android ViewId自动注解使用详解(ViewInject)
- Android|Android属性动画 Property animation
- JNI与底层调用-1
- #|FutureTask 使用场景介绍
- #|静态内部类创建单例的实现和优点
- #|View的三大流程是什么,加以简单说明