前言
上篇文章最简单的Activity、Service使用、通信指南一(进程内通信)(附github源码)最后提到,如果把Service设置为其他进程,那么我们使用自定义binder的方式返回的binder对象并不是我们自定义的binder对象,那样强制转换就报错误了。
那么为什么同一进程返回的是自定义binder对象,而跨进程返回的却不是自定义binder对象?BinderProxy是什么东西?
这篇文章主要是讲如何让Activity和Service跨进程通信,如果还没有看过Activity和Service同一进程通信,请看最简单的Activity、Service使用、通信指南一(进程内通信)(附github源码)
【转载请注明出处:最简单的Activity、Service使用、通信指南二(AIDL进程间通信)(附github源码) CSDN 王智博】
源码下载
github:https://github.com/samwangzhibo/LoveStudy (不会使用github导入代码的同学,戳这)
效果展示:
和最简单的Activity、Service使用、通信指南一(进程内通信)(附github源码)的例子一样,Service里面开启一个线程,每秒让自己的num变量加1,点击按钮,获取Service的num数值然后展示出来。
思路
这是需要和跨进程的Service连接,我们选择使用AIDL。
1.首先我们创建一个AIDL文件,用来说明我们定义的Service和Activity通信的接口
2.创建一个Service,onBind里面返回AIDL文件的Stub类的对象(这个对象实现了定义的接口)。
3.我们在Activity里面连接这个Service,获取数据展示
1.新建aidl文件
指定aidl文件的目录
新建IRemoteService.aidl文件,指定package和方法(只关注红框的内容)
他会在我们编译时生成IRemoteService.java的文件,我们看看里面有哪些东西.
可以看到多余生成了Stub的类,Stub还有一个Proxy的内部类.
2.创建Service,onbinder中返回IRemoteService.stub对象
3.Activity和Service连接,调用getNum接口获取数据
if(iRemoteService ==null)
{
Intent intent = new Intent(MainActivity.this, RemoteService.class);
bindService(intent, this, BIND_AUTO_CREATE);
commuteWithRemoteServiceBtn.setText("连接成功,点击获取跨进程service数据");
} else
{
try {
commuteWithRemoteServiceBtn.setText("获取service数据 : " + iRemoteService.getNum());
} catch (RemoteException e) {
e.printStackTrace();
}
}
这里获取onServiceConnected里面返回的service,然后通过IRemoteService.Stub.asInterface(service)把他转成IRemoteService的接口。
我们来看一下service是什么类型?IRemoteService.Stub.asInterface又有什么用?我们可以看出service是BinderProxy类型,和最简单的Activity、Service使用、通信指南一(进程内通信)(附github源码)返回的binder不一样(binder是onBinder里面返回的),为什么在跨进程的环境里面,就返回的是BinderProxy对象呢?
我们点开IRemoteService.Stub.asInterface方法,可以看到先调用queryLocalInterface,这个方法就是在同一进程的情况下获取自己,接下来的如果获取不到(跨进程),就把BinderProxy对象交给Stub的做一层Proxy代理。
public static com.example.wangzhibo.lovestudy.service.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//如果是同进程,直接返回本地对象
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.wangzhibo.lovestudy.service.IRemoteService))) {
return ((com.example.wangzhibo.lovestudy.service.IRemoteService) iin);
}
return new com.example.wangzhibo.lovestudy.service.IRemoteService.Stub.Proxy(obj);
}
然后就实现了第一次点击的时候,连接Service,第二次点击的时候getNum获取数据展示出来。
commuteWithRemoteServiceBtn.setText("获取service数据 : " + iRemoteService.getNum());
小结:
1.如果Service和Activity不在一个进程,我们发现onServiceConnection里面返回的就不是onBind()里面的对象,而是BinderProxy对象,这个时候我们需要用AIDL文件来帮助我们(当然也可以自己模仿AIDL写binder来实现)
2.AIDL文件实际上会生成java类,包含一个接口,一个Stub类,里面还有一个Proxy内部类。Activity使用Stub.asInterface获取接口对象,实际上是查询本地是否有binder,如果有就返回binder,或者就生成ProxyBinder的Stub.Proxy代理对象。
3.然后我们就能通过这个代理对象,调用接口,和远端Service通信了。
思路扩展:
1.这个Stub.Proxy何许人也?为啥能够和远端Binder通信?
2.为啥同进程情况下返回onBinde()的对象,跨进程情况返回BinderProxy而不是Binder对象?
3.每次我想要更新数据都要调用接口和服务端取数据,能不能实现服务端主动告知我数据变化,改变视图?
答案是肯定的。
下篇文章Activity传给Service回调,让Service主动修改Activity视图 三(AIDL进程间通信)(附github源码)主要实现一个Activity把回调传给Service,Service主动修改Activity视图的功能。