Android IPC机制—跨进程的观察者模式
在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机制(三)在Android Studio中使用AIDL实现跨进程方法调用
在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...
- Android中的跨进程通信方法实例及特点分析(一):AIDL Service
转载请注明出处:http://blog.csdn.net/bettarwang/article/details/40947481 近期有一个需求就是往程序中增加大数据的採集点,可是由于我们的Andro ...
- Android IPC机制(三)使用AIDL实现跨进程方法调用
上一篇文章中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户 ...
- Android IPC机制全解析<一>
概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...
- 深入理解Android IPC机制之Binder机制
Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...
- Android IPC机制基础
概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...
- android IPC 机制 (开发艺术探索)
一.IPC 机制介绍 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.那么什么是进程,什么是线程,进程和线程 ...
- Android IPC机制—Binder的工作机制
进程和线程的关系 IPC机制即为跨进程通信,是inter-Process Communication的缩写.是指两个进程之间进行通信.在说进程通信之前,我们的弄明白什么是线程,什么是进程.进程和线程是 ...
- Android随笔之——跨进程通信(一) Activity篇
在Android应用开发中,我们会碰到跨进程通信的情况,例如:你用QQ通讯录打电话的时候会调用系统的拨号应用.某些新闻客户端可以将新闻分享到QQ.微信等应用,这些都是跨进程通信的情况.简而言之,就是一 ...
随机推荐
- mysql实现随机获取几条数据的方法
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/xionglangs/article/details/50630758sql语句有几种写法 1:SEL ...
- JS-1
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- mysql的报错
这个错误是因为mysql的进程错误关闭导致的,我们需要把/var/lib/mysql/mysql.sock文件删除或者改名,之后再重启就ok
- 《推荐》安装Photoshop详细步骤 ,手把手,一步一步,具体详细地教你安装Photoshop (Adobe photoshop CS6)
现在的大学生必备技能,除了单反拍照,就是PS (Photoshop)了.可以说PS是一个强大的图片编辑处理软件,也是目前公认的最好的通用平面美术设计软件,它的功能完善,性能稳定,使用也很方便.几乎在所 ...
- raft共识算法
raft共识算法 分布式一致性问题 如果说,服务器只有一个节点,那么,要保证一致性,没有任何问题,因为所有读写都在一个节点上发生.那如果server端有2个.3个甚至更多节点,要怎么达成一致性呢?下面 ...
- JAVA第3,4课(内容合并)
JAVA 第三课 代码执行顺序
- Android 偏门xml属性
在 recycleView listview scroview 等等 活动的时候会出现蓝边 android:overScrollMode="never" 用次属性可以去掉 fadi ...
- 用usecase获取需求的方法是否有缺陷,还有什么地方需要改进
usecase的局限性 对于系统发展而言,Use Case的范围限制一个单一的系统,这是Use Cases最通常的形式,我们称之为System Use Case,它把整个系统看作是一个黑盒,它不指定任 ...
- 最全面的Redis命令行查阅手册(收藏查看)
Redis是用C语言实现的,一般来说C语言实现的程序“距离”操作系统更近,执行速度相对会更快. Redis使用了单线程架构,预防了多线程可能产生的竞争问题. 作者对于Redis源代码可以说是精打细磨, ...
- ORACLE取字段中的注释
select * from (SELECT 'comment on column '|| t.table_name||'.'||t.colUMN_NAME||' is '|| ''''||t1.COM ...