在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. 基于Blod的ajax进度条下载实现

    普通的浏览器下载 在web开发中,如果要实现下载功能,往往都是使用新开web页面或者是使用iframe的形式.实现起来其实很简单: <a target="_blank" hr ...

  2. 获取LayoutInflater对象的方法和inflate方法的一些参数问题

    一.获取LayoutInflater的三种方法 1. LayoutInflater layoutInflater = (LayoutInflater) MainActivity.this.getSys ...

  3. 原生js中slice()方法和splice()区别

    slice()方法和splice()方法都是原生js中对数组操作的方法. slice(),返回一个新的数组,该方法可从已有的数组中返回选定的元素.例如:arrObject(start,end),sta ...

  4. [转载]解决win10 VC++6.0 应用程序无法正常运行 0xc0000142

    本文转载自http://blog.csdn.net/w_9449/article/details/52864135 转载请申明哦,其实我发现自从我在贴吧发了帖子后,就冒出了不少帖子.经验.当然方法和我 ...

  5. 利用smarty模板(登录、有关信息操作等功能)

    smarty模板前提是:前端和后端是分开的,所以肯定会有很多的后台页面,php页面和html页面是分开存储的. (1)登录页面的编写也是分两个页面(后台和前端) 1.首先是后台的php页面,很简单只要 ...

  6. 关于pthread_create未定义的引用的奇葩解决

    1本来在代码里包含了pthread.h  第一次编译:gcc -o create create.c  提示错误 未定义的引用 2后来知道linux标准库没有pthread.h 要链接之-lpthrea ...

  7. 9 个用于移动APP开发的顶级 JavaScript 框架

    顶级 Java 框架 对于Web开发而言,Java是一个有前途的编程语言,并且在不久的将来它将依然在这个领域大放光彩.Java在移动app开发上也有同样的影响吗?让我们一起来看看ValueCoders ...

  8. 2017年的golang、python、php、c++、c、java、Nodejs性能对比(golang python php c++ java Nodejs Performance)

    2017年的golang.python.php.c++.c.java.Nodejs性能对比 本人在PHP/C++/Go/Py时,突发奇想,想把最近主流的编程语言性能作个简单的比较, 至于怎么比,还是不 ...

  9. 入坑系列之HAProxy负载均衡

    在大型系统设计中用代理在负载均衡是最常见的一种方式,而相对靠谱的解决方案中Nginx.HAProxy.LVS.F5在各大场中用得比较普遍,各有各的优势和使用场景,由于本次要使用到TCP,因此Nginx ...

  10. QT 中使用 c++ 的指针

    之前没有接触过 c++,不过听说 c++ 的指针很坑,直到最近在用 QT / C++ 写一个 Linux Deepin 系统上检测网络流量和网速的小程序时,发现 c++ 的指针用起来真的特别蛋疼. 不 ...