一:aidl与messenger的使用区别
- aidl与messenger都是使用在进程间通信的方式,底层都是Binder。不是进程间通信的可以直接使用Binder。
- messenger底层使用的是aidl结构。
- messenger服务端串行执行任务。
- aidl可以实现并发操作,但是要注意线程安全。aidl还可以调用远程服务的方法,messenger则不可以。
- aidl的同步主要是在aidl接口在service中的实现时添加。
二:一个简单的aidl创建流程
(一):创建aidl文件

(二):创建远程服务
注意aidl接口的方法,是需要线程安全的。

(三):将远程的aidl包拷贝到本地项目
后面还会介绍如何使用parcelable对象,其中实现parcelable对象的类,也需要写一个aidl文件。

(四):绑定远程服务
在调用aidl接口方法的时候,需要注意这个方法是耗时操作。

三:远程启动服务的注意事项
aidl一般是用在两个进程中,如果使用在两个app中,那么要怎么从一个app启动另一个app的远程服务呢?在android 5.0之后,系统规定要使用显示启动所有的service,否则就会抛出异常。因此就有下面两种启动方式
(一)设置过滤器,google并不建议我们这么做
java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.xia }
<service android:name=".MyService"
>
<intent-filter>
<action android:name="com.xia"/>
</intent-filter>
</service>
intent.setAction("com.xia");
intent.setPackage("xiaguangcheng.com.local");
由于service设置了过滤器,那么exported就默认为true了。
(二)使用全类名打开service,主要要把允许其他app使用设置为true
java.lang.SecurityException: Not allowed to bind to service Intent { cmp=xiaguangcheng.com.local/.MyService }
<service android:name=".MyService"
android:exported="true">
</service>
intent.setComponent(new ComponentName("xiaguangcheng.com.local","xiaguangcheng.com.local.MyService"));
由于没有设置过滤器,因此需要显示指定exproted=true。

四:创建一个略复杂的aidl
(一)aidl支持的数据类型
- 基本数据类型
- String和CharSequence
- ArrayList,元素必须被aidl支持
- HashMap,元素必须被aidl支持
- Parcelable,所有实现了Parcelable接口的对象
- aidl接口本身也能在aidl文件中使用
(二)使用parcelable接口的对象
- 我们要在java包中创建一个Book.java文件实现parcelable接口
- 我们还要在aidl包中创建一个同名的aidl文件Book.aidl,并在其中声明
parcelable Book;
同时手动导入Book.java的包名。
(三)使用RemoteCallbackList删除跨进程接口
public class BookManagerService extends Service {
public static final String TAG="BMS";
//读写分离,最终一致性,保障并发安全
private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();
private AtomicBoolean mIsServiceDestoryed=new AtomicBoolean(false);
//解决因为aidl的序列化问题,导致传递过来的对象,已非客户端的对象。这个类则解决了这个问题。
private RemoteCallbackList<IBookManagerNewBookArravied> mListenerList=
new RemoteCallbackList<>();
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book("西游记",111));
mBookList.add(new Book("红楼梦",222));
new Thread(new ServiceWorker()).start();
}
private Binder mBinder=new IBookManager.Stub(){
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void registerListener(IBookManagerNewBookArravied listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unRegisterListener(IBookManagerNewBookArravied Listener) throws RemoteException {
mListenerList.unregister(Listener);
}
};
public BookManagerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while(!mIsServiceDestoryed.get()){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId= mBookList.size()*111;
Book newBook=new Book("bookName:"+bookId,bookId);
try {
onNewBookArrrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
private void onNewBookArrrived (Book newBook)throws RemoteException {
mBookList.add(newBook);
final int N=mListenerList.beginBroadcast();
for(int i=0;i<N;i++){
IBookManagerNewBookArravied iBookManagerNewBookArravied = mListenerList.getBroadcastItem(i);
if(iBookManagerNewBookArravied!=null){
iBookManagerNewBookArravied.onNewBookArrived(newBook);
}
}
mListenerList.finishBroadcast();
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
}
五:如何判断Binder连接是否中断
在绑定服务完成之后,我们要给binder设置一个死亡代理。即通过IBinder.linkToDeath(mDeathRecipient,0)
来设置。其中这个死亡接收者有一个binderDied的回调。当binder中断,就需要在这个回调中,取消之前绑定死亡代理的那个binder,重新绑定服务,绑定死亡代理。
IM im;
private IBinder.DeathRecipient mDeathRecipient=new IBinder.DeathRecipient(){
@Override
public void binderDied() {
if(im==null){
return;
}
im.asBinder().unlinkToDeath(mDeathRecipient,0);
im=null;
connect();
}
};
……
public void connect(){
Intent intent=new Intent("com.abc");
intent.setPackage("com.xiaguangcheng.aidl");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
im=IM.Stub.asInterface(service);
try {
service.linkToDeath(mDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
},BIND_AUTO_CREATE);
}
网友评论