更新时间 | 修改意见 |
---|---|
2016-08-02 | 陈敏 |
第4节 远程调用
之前提到过:如果站在Service与触发Service运行的那个组件的角度,根据它们的关系进行分类,有两种:本地Service,远程Service。
本地Service就是同一个应用的组件的调用本地应用中的Service组件;
远程Service就是其它应用的组件跨进程调用其它应用中的Service组件。
对于使用Start Service
的方式远程运行Service
是很简单的。和本地Service几乎完全一样,只是要采用隐式调用的方式调用。
这里主要讲讲跨进程通过Bind Service
的方式远程绑定Service
。
跨进程调用自定义Service
有两种方式:Messager和AIDL。要让两个不同的进程之间进行函数调用,就要使用进程间通信IPC,这两种方式都使用了IPC技术。在安卓系统当中,它实际上是由Binder
来实现的。
4.1 AIDL实现进程间调用
在源码目录下创建一个以
aidl
为后缀的文件,例如IRemoteCall.aidl
,将Service
要提供给其他进程使用的接口函数定义在里面,例如,package xxx.xxx.xxx; interface IRemoteCall { void remoteFunc(int param); }
Android Studio编译器会根据AIDL接口文件,自动生成对应的java源代码。它产生的java类可以直接拿来使用。
继承
Service
类,创建自己的Service
,并实现由IRemoteCall.aidl
定义的Binder
,public class MyService extends Service { ...... //实现IRemoteCall.aidl定义的Binder-IRemoteCall.Stub()由编译器自动产生 private final IBinder mBinder = new IRemoteCall.Stub() { @Override public void remoteFunc(int param) throws RemoteException { //调用Service中真正实现功能的方法 innerRemoteFunc(param); } }; //真正实现功能的方法 private void innerRemoteFunc(int param) { } @Override public IBinder onBind(Intent intent) { //当组件bindService()之后,将这个Binder返回给组件使用 return mBinder; } ...... }
在
AndroidManifest.xml
文件中,用隐式的方式声明新创建的Service
;<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.anddle.lifetime"> ...... <application ...... android:theme="@style/AppTheme"> ...... <!--声明新创建的Service--> <service android:name=".MyService" android:enabled="true" android:exported="true" ---设置成true > <!--指定一个过滤器,为过滤器指定一个Action name--> <intent-filter> <action android:name="custom.service.remote" /> </intent-filter> </service> </application> </manifest>
这里要把
android:exported
属性设置成true
,其他进程中的组件才能够使用它,否则只有同一个进程的组件能使用。
在另一个应用中,要远程调用Service
也很简单,
创建一个
ServiceConnection
,当绑定Service
之后在onServiceConnected()
中会得到Service
返回的Binder
;如果Service遇到异常情况退出时,会通过onServiceDisconnected
通知已经绑定过它的组件,绑定断开。如果用户主动解除绑定,这个
onServiceDisconnected()
是不会被触发的。private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //这里的service参数,就是Service当中onBind()返回的Binder IRemoteCall remoteCall = IRemoteCall.Stub.asInterface(service); try { //通过AIDL中定义的接口-IRemoteCall,就可以调用到Service提供到函数了 remoteCall.remoteFunc(0); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { //当Service遇到异常情况退出时,会通过这里通知已经绑定过它的组件 } };
假设Activity A中有个按钮,点击之后就用隐式调用的方式调用
bindService
;还有个按钮B,点击之后就调用unbindService
。
可以看到,通过AIDL进行远程调用与不使用远程调用基本一样,只是它们产生和获取Binder
的方式不同,
- 远程调用是通过AIDL产生
Binder
; - 非远程调用是通过继承
Binder
类产生Binder
;
4.2 Messenger实现进程间调用
Messenger
方式是一种进程间消息传递到方式,可以让组件B发送消息M到Service,让Service根据消息的类型进行相关的操作,
在
Service
中,创建一个内部的Handler类
,public class MessengerService extends Service { static final int MSG_REMOTE_FUNC = 1; class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REMOTE_FUNC: //获取到组件A发出的命令 innerRemoteFunc(msg.arg1) break; default: super.handleMessage(msg); } } } //真正实现功能的方法 private void innerRemoteFunc(int param) { ...... } ...... }
创建一个用来传递消息的Messenger,将Messenger作为Binder返回给调用者,今后调用者就可以用这个给Service发消息了。消息的内容在
IncomingHandler
的handleMessage()
函数中得到。public class MessengerService extends Service { ...... //创建一个用来传递消息的Messenger final Messenger mMessenger = new Messenger(new IncomingHandler()); @Override public IBinder onBind(Intent intent) { //将Messenger作为Binder返回给调用者,今后调用者就可以用这个给Service发消息了, //消息的内容在`IncomingHandler`的`handleMessage()`函数中得到 return mMessenger.getBinder(); } ...... }
组件A那边在使用的时候可以,
创建一个
ServiceConnection
,当绑定Service
之后在onServiceConnected()
中会得到Service
返回的Binder
;如果Service遇到异常情况退出时,会通过onServiceDisconnected
通知已经绑定过它的组件,绑定断开。private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //这里的service参数,就是Service当中onBind()返回的Messenger的Binder, //这里我们通过这个Binder,把它还原成一个可以向Service发送消息的Messenger Messenger remoteCall = new Messenger(service); //通过Messenger,就可以向Service发送消息了 Message msg = Message.obtain(null, MessengerService.MSG_REMOTE_FUNC, 0, 0); try { remoteCall.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { //当Service遇到异常情况退出时,会通过这里通知已经绑定过它的组件 } };
假设Activity A中有个按钮,点击之后就用隐式调用的方式调用
bindService
;还有个按钮B,点击之后就调用unbindService
。
这样一来,组件A就可以向Service
发送消息了。Service
一收到消息,就会根据消息的类型,去执行对应的操作了。
可以看出,
- 因为对
Service
操作的请求是通过Handler
进行的,所以组件们请求都会按照先来后到一个一个顺序执行; - 只有其它组件可以向
Service
发送执行某个操作的消息,而Service
无法主动回报数据。
4.3 AIDL与Messenger怎么选
使用Messenger
要比使用AIDL
更简单,Messenger
会将所有调用排入队列,按照顺序一个一个执行;而AIDL
方式允许多个组件同时向Service
发送请求,所以Service
需要考虑同步的问题。
对于大多数应用,Service
不需要执行多线程处理,不需要数据的主动回报,因此使用Messenger
可让服务一次处理一个调用;否则就使用AIDL
方式吧。
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/