参考《Android开发艺术探索》
我的测试环境是在两个app下,当然也可以在同一个app中的两个进程。
记录这篇文章,是为了不忘记使用aidl,因为在项目中用的比较少。
IPC通信 : AIDL、 Bundle、Message 、 Contentprovider、文件共享,socket。
首先我们先创建客户端的代码。
客户端 :
如果需要传输自定义的对象
自定义对象的传递需要序列化所以要实现 Parcelable 。
public class Book implements Parcelable
然后需要在和mian中的同级下创建一个aidl的包
其中的包名结构要和java代码的结构一致。
在创建Book类的时候 需要同时创建一个Book的aidl文件(包名一致)(创建的时候可能会让你在aidl名字前加I,没关系,创建后修改就好 。因为aidl文件要和Book 文件名 一致)
同时我也创建了IBooKAidlInterface, 和 IBookListener 类
需要注意的是 当你运用 自定义对象传递的时候 , 你需要import 它, 同时需要加上参数
In、out、inout 中的一个 ,具体选择 要看你的需求( 要好好选择 因为他的开销还是蛮大的)。
同时写了一个监听器来监听数据的变化
Ok,到这里创建了所需要的AIDL文件。
然后可以去Make Project 你的项目。会在build下生成一个文件,就可以用这个文件来操作拉。
然后在 客户端的Activity中去绑定服务(要是bindService~):如果不是在同一个app下 可以这样去bind它:
申请aidl的监听器:
(需要注意的是 在 onAddBooks 中 的动作 其实是在Binder池中的,不是在主线程中的,所以如果需要操作UI的话 需要handler出去)
在获取到binder的时候 我注册了 监听器 和绑定了bind的死亡,为什么这样做呢 , 是为了防止 bind死亡的时候,我们不知道 然后继续去操作导致异常 ,当使用linkToDeath去绑定binder的时候,只要bind死亡了 会通知我们 。
然后实现下面的方法:
实现监听器之后,记得在退出的时候 注销他。
同时还有需要注意的是:
当你调用某个方法的时候,你要特别小心他可能会造成阻塞,因为服务端的代码可能需要比较长的时间来完成,如果你在UI线程中去调用他,会造成ANR 。 所以尽量在子线程中调用。
Ok ,接下来就是服务端了:
首先 要将 客户端的aidl整个结构原封不动的复制过来。同时自定义的对象也要在一个和客户端同样的包名下。
Make 一下 也会生成 和客户端同样的代码。
然后绑定Service后返回binder 对象:
IBinder binder = new IBooKAidlInterface.Stub() {
@Override
public void addBook(Book book) throws RemoteException {
lists.add(book);
onAddBook(book );
}
@Override
public List<Book> getBooks() throws RemoteException {
return lists ;
}
@Override
public void registerListener(IBookListener listener) throws RemoteException {
callbackList.register(listener);
}
@Override
public void unregisterListener(IBookListener listener) throws RemoteException {
callbackList.unregister( listener );
}
};
在registerListener中 有一个list 去 register他。
这个list 是 ,这个类是特意为aidl的监听使用的
private RemoteCallbackList<IBookListener> callbackList = new RemoteCallbackList<>();
但是 这个list的调用 需要如下:
private void onAddBook(Book book){
int size = callbackList.beginBroadcast() ;
for (int i = 0; i< size ; i++){
try {
callbackList.getBroadcastItem(i).onAddBooks(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
callbackList.finishBroadcast();
}
现在只要你在onBind中 返回binder 对象 ,让客户端连接上服务端 就可以进行通信了。
但是 服务端不能谁来连接都运行把。所以我们可以设置权限验证。
首先
要设置 权限:
然后在客户端:
<uses-permission android:name="com.austin.aidlserver" />
在Service中进行验证:
- 在onBind中验证
@Nullable
@Override
public IBinder onBind(Intent intent) {
int i = checkCallingOrSelfPermission("com.austin.aidlserver");
if (i == PackageManager.PERMISSION_DENIED){
return null ;
}
return binder;
}
2.在 onTransact中验证:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// 在这里权限验证
int check = checkCallingOrSelfPermission("com.austin.aidlserver");
if(check==PackageManager.PERMISSION_DENIED){
return false;
}
// 包名验证
String packageName=null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if(packages!=null && packages.length>0){
packageName = packages[0];
}
if(!packageName.startsWith("com.austin")){
return false;
}
return super.onTransact(code, data, reply, flags);
}
Ok 这样就大功告成了~~
其实里面还有很多有意思的原理 可以去探索,这里我都没有写出来,比如为什么要用RemoteCallbackList等,如果感兴趣的话,可以自己去探索一下啦~