【Android 笔记】AIDL 实现 IPC
AIDL是 Android 定义语言,可用其实现跨进程的数据传输。AIDL 的定义和 Java 的接口定义相似。
AIDL 和 Messenger
- AIDL 是 Messenger 的底层实现
- Messenger 只能顺序执行,不适合并发的情况
Messenger 不支持跨进程调用其他进程方法,而 AIDL 可以
AIDL 支持的数据类型
基本类型、CharSequence、List(ArrayList)、Map(HashMap)、 Parcelable、AIDL
AIDL 的组成
- AIDL 接口
定义一 AIDL 接口和方法,这个接口时服务端面向客户端的接口,这个接口在服务端中实现。在定义参数时还要加上:in、out、inout 表示输入还是输出 - 服务端
实现 AIDL 接口,在客户端绑定服务时(onBind() 方法)中返回该 AIDL 的实现,供客户端调用 - 客户端
发起绑定服务,获得服务端传回的对象,可调用该对象提供的方法
使用
客户端调用服务端
1)这里传输一个自定义的对象 Book,为了让它 Book 能被 AIDL 传输,实现 Parcelable 接口,然后在 AIDL 文件中声明。
public class Book implements Parcelable{
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
//从序列化后的对象中创建原始对象
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
Log.v("_v", "反序列化 " + bookId + " " + bookName);
}
//反序列化
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];
}
};
//内容描述,含有文件描述符返回1.否则为0
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(Parcel parcel, int i) {
Log.v("_v", "序列化 " + bookId + " " + bookName);
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}
//Book.aidl 文件
parcelable Book;
2)创建 AIDL 接口文件
//需要显式地导入Book.aidl
import cn.vecrates.ipc.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
3)服务端实现接口方法 (还要在 Manifest 中指定 android:process)
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private IBinder mBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return mBookManager;
}
}
4)客户端实现,调用服务端方法
public class BookManagerActivity extends AppCompatActivity {
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
bookManager.addBook(new Book(1, "android"));
bookManager.addBook(new Book(2, "ios"));
List<Book> list = bookManager.getBookList();
Log.v("_v", "客户端获取到服务端数据 " + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
增加服务器调用客户端
不知道这么说合不合适,不过我是这么理解的。服务端调用客户端实际上定义了一个 AIDL,然后在客户端实现这个接口,客户端把接口对象传给给服务端,服务端调用这个接口对象的方法。
1)在上面的基础上添加一个接口,以及增加原接口两个方法
//IOnBookArrivedListener.aidl
import cn.vecrates.ipc.aidl.Book;
interface IOnBookArrivedListener {
void onNewBookArrived(in Book book);
}
//IBookManager.aidl
import cn.vecrates.ipc.aidl.Book;
import cn.vecrates.ipc.aidl.IOnBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnBookArrivedListener listener);
void unregisterListener(IOnBookArrivedListener listener);
}
2)客户端实现接口
public class BookManagerActivity extends AppCompatActivity {
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.v("_v", "客户端 连接成功");
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
bookManager.addBook(new Book(1, "android"));
bookManager.addBook(new Book(2, "ios"));
List<Book> list = bookManager.getBookList();
// Log.v("_v", "客户端获取到服务端数据 " + list.toString());
//在服务端中注册
bookManager.registerListener(mOnBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {}
};
private IOnBookArrivedListener mOnBookArrivedListener = new IOnBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
Log.v("_v", "客户端收到通知 " + book.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
3)服务器实现。
原书籍 Demo 是想实现一个观察者模式,在服务端中使用 List 存储客户端实现的接口对象。由于 List.remove() 无法移除里面对象,所以使用 RemoteCallbackList
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//存储客户端的监听对象,为了能移除其中的对象只能用RemoteCallbackList
private RemoteCallbackList<IOnBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private IBinder mBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(IOnBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
Log.v("_v", "客户端注册成功");
//客户端注册成功后通知客户端
Book book = new Book(001, "这是一本新书");
notifyClients(book);
}
@Override
public void unregisterListener(IOnBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
Log.v("_v", "客户端解除注册");
}
};
//通知所有客户端(这儿只有一个客户端)
private void notifyClients(Book book) {
//RemoteCallbackList 遍历的的形式
Log.v("_v", "通知客户端 " + book.toString());
int size = mListenerList.beginBroadcast();
for (int i=0; i<size; i++) {
IOnBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if(listener != null) {
try {
//调用客户端
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
@Override
public void onCreate() {
super.onCreate();
Log.v("_v", "服务端 onCreate()");
}
@Override
public IBinder onBind(Intent intent) {
return mBookManager;
}
}
5)结果
更新 UI 注意事项
不在 UI 线程中访问服务端的耗时方法,否则可能 ANR
被调用的方法运行在服务器的 Binder 线程池,调用者(客户端)线程会被挂起直至该方法执行完毕,所以如果服务端方法比较耗时,将会 ARN服务端不应该调用客户端中在 UI 线程中的耗时方法(可以运行在非 UI 线程),否则可能导致服务端无响应
重新连接服务的方法
- 设置 DeathRecipient 监听
- 在ServiceConnection 的 onServiceDisconnected() 中重新连接
这两种方式前者运行在客户端 Binder 线程池中,后者运行在 UI 线程中
AIDL 权限验证
- 在 onBind() 中验证
- 在 onTransact() 中验证