在AIDL文件中并不是所有的数据类型都可以使用,AIDL支持的数据类型如下:

  • 基本数据类型(int、long、char、boolean、double等)
  • String和CharSequence
  • List:只支持HashMap,HashMap的key和value的类型都必须被AIDL支持
  • Parcelable:所有实现了Parcelable接口的对象
  • AIDL:AIDL接口本身也可以在AIDL文件中使用

要注意的是,自定义的Parcelable对象和AIDL对象必须要显示import进来,不管他们是否和当前的AIDL文件位于同一包内。比如IBookManager.aidl这个文件里面用到了BookEntity这个类,BookEntity这个类实现了Parcelable接口,虽然他们位于同一包中但必须遵循AIDL规范,仍然要显式的import进来。另外,上一篇文中也提到过,如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

AIDL中实现了Parcelable接口的类都需要按照上面的方式去创建相应的AIDL文件并声明它为Parcelable类型。除此之外,AIDL中除了基本数据类型,其他类型的参数必须表上in、out或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出参数。最后,AIDL接口中只支持方法,不支持声明静态常量。

为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放在一个包中,这样做的好处是,当客户端是营外一个应用时,我们可以直接把整个包复制到客户端工程中。需要注意的是AIDL的包结构在服务端和客户端要保持一致,否则运行出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一致的话,就无法成功反序列化,程序也就无法正常运行。

在示例中,服务端Service中采用了CopyOnRightArrayList,这个CopyOnRightArrayList支持并发读写,由于AIDL方法是在服务端的Binder线程池中执行的,因此当有多个客户端同时连接的时候会存在多个线程同时访问的情形,所以我们需要在AIDL方法中处理线程同步,这里使用CopyOnRightArrayList来进行自动的线程同步。

接下来在之前示例上继续完善,在客户端给服务端添加一本书然后再获取一次看看程序是否正常工作。客户端在服务连接后,在onServiceConnected中调用addBook(),再次查询会发现添加成功了。

现在考虑一种情况:用户不想时不时的去查询图书列表,于是他告诉图书管理人员"当有新书时把图书信息告诉我",这是一种典型的观察者模式。首先,我们需要提供一个AIDL接口,每个用户需要实现这个接口并且想图书馆申请新书的订阅功能,之所以选择AIDL接口而不是普通接口,是因为在AIDL中无法使用普通接口。新建一个IOnNewBookArrivedListener.aidl,我们所期望的是当Service中有新书到来时,就会通知每一个已经申请了通知的用户。

IOnNewBookArrivedListener.aidl:

IBookManager.aidl:

接下来,在服务端中也需要作修改,主要是Service中IBookManagre.Stub的实现,同时在BookManagerService中开启一个线程,每隔5秒向书库中增加一本书并通知所有订阅者。

服务端BookManagerService.java:

// CopyOnWriterArrayList支持并发读写,AIDL方法是在服务端的Binder线程池中执行的,因此多个客户端同时连接的
// 时候会存在多个线程同时访问的情形
private CopyOnWriteArrayList<BookEntity> mBookList = new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
private AtomicBoolean isServiceDestoryed = new AtomicBoolean(false);
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<BookEntity> getBookList() throws RemoteException {
return mBookList;
} @Override
public void addBook(BookEntity book) throws RemoteException {
mBookList.add(book);
} @Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
if (!mListenerList.contains(listener)) {
mListenerList.add(listener);
} Logger.d("registerListener: " + mListenerList.size());
} @Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
if (mListenerList.contains(listener)) {
mListenerList.remove(listener);
} Logger.d("unRegisterListener: " + mListenerList.size());
}
}; @Override
public void onCreate() {
super.onCreate();
mBookList.add(new BookEntity("1", "Android从后门到入门"));
mBookList.add(new BookEntity("2", "iOS从后门到前门"));
new Thread(new AddBookWorker()).start();
} @Override
public void onDestroy() {
isServiceDestoryed.set(true);
super.onDestroy();
} @Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
} private class AddBookWorker implements Runnable { @Override
public void run() {
while (!isServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
BookEntity bookEntity = new BookEntity(bookId + "", "new Book#" + bookId);
try {
onNewBookArrived(bookEntity);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
} private void onNewBookArrived(BookEntity bookEntity) throws RemoteException {
mBookList.add(bookEntity);
for (int i = 0; i < mListenerList.size(); i++) {
IOnNewBookArrivedListener listener = mListenerList.get(i);
listener.onNewBookArrived(bookEntity);
}
}
客户端BookManagerActivity.java:
public class BookManagerActivity extends AppCompatActivity {
private static final int NEW_BOOK_ARRIVED = 1; // 客户端需要注册IOnNewBookArrivedListener到远程服务端,当有新书时服务端才能通知客户端
// 在Activity退出时解除注册 private IBookManager mRemoteBookManager;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == NEW_BOOK_ARRIVED) {
Logger.d("new book arrived: " + msg.obj);
}
}
}; private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定成功后将Binder对象转换成AIDL接口
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
BookEntity bookEntity = new BookEntity("3", "Android AIDL");
bookManager.addBook(bookEntity);
List<BookEntity> list = bookManager.getBookList();
for (BookEntity book : list) {
Logger.d(book.getBookId() + "," + book.getBookName());
}
bookManager.registerListener(mNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
}
}; private IOnNewBookArrivedListener mNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(BookEntity newBook) throws RemoteException {
mHandler.obtainMessage(NEW_BOOK_ARRIVED, newBook.getBookId()).sendToTarget();
}
}; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
} @Override
protected void onDestroy() {
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
try {
mRemoteBookManager.unRegisterListener(mNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mServiceConnection);
super.onDestroy();
}
}

特别注意的是,打印Log测试时一定注意切换进程查看日志。

到这里对AIDL也有了一个初步的认识,但是AIDL还远远不止这些,通过文字总结再结合代码慢慢琢磨一定能慢慢理解AIDL这种实现方式。

Android IPC机制全解析<二>的更多相关文章

  1. Android IPC机制全解析<一>

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

  2. Android异步载入全解析之使用多线程

    异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutC ...

  3. Android异步载入全解析之IntentService

    Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个Intent ...

  4. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  5. Android异步载入全解析之大图处理

    Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...

  6. Android异步载入全解析之使用AsyncTask

    Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...

  7. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  8. android IPC 机制 (开发艺术探索)

    一.IPC 机制介绍 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程和线程 ...

  9. Handler消息机制与Binder IPC机制完全解析

    1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...

随机推荐

  1. ABP 学习汇总

    本文背景 公司最近规划的新框架准备基于ABP来搭建,自从在阳铭博客看到ABP框架的介绍后,就一直持续关注着,但还没真正在实际项目中直接使用ABP,只是自己做了一些学习和Demo.ABP所用到的一些新技 ...

  2. 每天一个linux命令(36)--vmstat命令

    vmstat 是 Virtual Memory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.他是对系统的整体情况进行统计,不足之处是无法对某个进程进行 ...

  3. MySQL学习分享--数值类型

    数值类型 MySQL的数值类型包括整数类型.浮点数类型.定点数类型.位类型. 整数类型 MySQL支持的整数类型有tinyint.smallint.mediumint.int.bigint(范围从小到 ...

  4. Alamofire源码解读系列(三)之通知处理(Notification)

    本篇讲解swift中通知的用法 前言 通知作为传递事件和数据的载体,在使用中是不受限制的.由于忘记移除某个通知的监听,会造成很多潜在的问题,这些问题在测试中是很难被发现的.但这不是我们这篇文章探讨的主 ...

  5. Chosen通用初始化

    一直在用Chosen这个js插件,其目的就是美化下拉框.github地址:https://harvesthq.github.io/chosen/ no_results_text:"xxxxx ...

  6. jQuery的ajax详解

    很多朋友都喜欢用JQ 而ajax更是JQ里必不可少的 下面为大家详细介绍一下JQ的ajax 首先 什么是ajax: AJAX = 异步 JavaScript 和 XML(Asynchronous Ja ...

  7. AR入门系列-07-Vuforia柱形体识别

    今天为大家带来Vuforia柱形体识别的使用教程 首先我们要进入Vuforia官网在TargetManager中添加Target,这次我们添加的类型为Cylinder圆柱 Bottom Diamete ...

  8. Java 数值类型以及计算

    前段时候写了一个对外提供的接口,其中有一个数值校验的计算.在测试的过程中发现5.6-1.6 != 4,在反复的测试过程中发现double类型的数值为有精度丢失的现象,看来还是基础知识不牢固,所以就在网 ...

  9. 1644: [Usaco2007 Oct]Obstacle Course 障碍训练课

    1644: [Usaco2007 Oct]Obstacle Course 障碍训练课 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 383  Solved ...

  10. Linux(CentOS6.7) 安装MySql5.7数据库

    linux(CentOS6.7) 环境Mysql 5.7.17安装教程分享给大家,供大家参考,具体内容如下: 1系统约定安装文件下载目录:/data/softwareMysql目录安装位置:/usr/ ...