在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机制全解析<二>的更多相关文章
-
Android IPC机制全解析<;一>;
概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...
-
Android异步载入全解析之使用多线程
异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutC ...
-
Android异步载入全解析之IntentService
Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个Intent ...
-
Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用
在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...
-
Android异步载入全解析之大图处理
Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...
-
Android异步载入全解析之使用AsyncTask
Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...
-
深入理解Android IPC机制之Binder机制
Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...
-
android IPC 机制 (开发艺术探索)
一.IPC 机制介绍 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程和线程 ...
-
Handler消息机制与Binder IPC机制完全解析
1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...
随机推荐
-
HttpRequestUtil
package com.didichuxing.tempdirreader; import com.alibaba.fastjson.JSONObject; import java.io.Unsupp ...
-
uva167 The Sultan&#39;s Successors
The Sultan's Successors Description The Sultan of Nubia has no children, so she has decided that the ...
-
JQuery实现 checkbox 全选、反选,子checkbox有没选去掉全选
1. 全选的checkbox选中时,子checkbox全部选中.反之,全部不选 2.子checkbox中,只要有没有被选中的,取消全选checkbox的选中 3.子checkbox的数量和子check ...
-
PHP数据访问易错点(20161030)
易错点: 1.造对象的时候括号里面的参数 写错了 $db = new MySQLi("localhost","root","789",&qu ...
-
python scrapy 抓取脚本之家文章(scrapy 入门使用简介)
老早之前就听说过python的scrapy.这是一个分布式爬虫的框架,可以让你轻松写出高性能的分布式异步爬虫.使用框架的最大好处当然就是不同重复造*了,因为有很多东西框架当中都有了,直接拿过来使用就 ...
-
对于vxworks下硬盘驱动
1.曾经看到帖子说vxworks5.5下没有sata驱动,vxworks6.6下有,这样的说法恐怕不正确,由 于俺在5.5下也运用运用了sata硬盘,请注重这里俺只是说运用运用,没有说运用运用了sat ...
-
Ionic APP-Web SPA开发进阶(二)Ionic进阶之路由去哪了
Ionic进阶之路由去哪了 项目需求 在查看药品时,从药品列表中可以通过点击药品列表获取某一药品详情.提交订单时,同样可以查看药品详情.两种情形下,从药品详情返回后,应分别返回至原来的页面.如下图所示 ...
-
49_分析代理类的作用与原理及AOP概念
生活中的代理 武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里 ...
-
emouse思·;睿&mdash;评论与观点整理之一
虽说我主要做的硬件,平时的兴趣爱好比较关注移动互联网,混迹于虎嗅.爱范儿.雷锋网.36Kr.cnBeta.瘾科技.i黑马.TechWeb等这类科技以及创业媒体,遗憾的是系统的去写的并不多,好在还算充分 ...
-
mvc基础知识(1)
复制大佬的,侵权请联系我主动删除 1.js/css合并 在之前的crud例子中,我们引入js/css脚本的方式和平常的web开发一样 <script src="~/Scripts/jq ...