四大组件之Service(三)-Service的跨进程调用

来源:互联网 发布:java 锁修饰静态方法 编辑:程序博客网 时间:2024/04/29 10:44
更新时间 修改意见 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实现进程间调用

  1. 在源码目录下创建一个以aidl为后缀的文件,例如IRemoteCall.aidl,将Service要提供给其他进程使用的接口函数定义在里面,例如,

    package xxx.xxx.xxx;interface IRemoteCall {    void remoteFunc(int param);}

    Android Studio编译器会根据AIDL接口文件,自动生成对应的java源代码。它产生的java类可以直接拿来使用。

  2. 继承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;    }    ......}
  3. 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也很简单,

  1. 创建一个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遇到异常情况退出时,会通过这里通知已经绑定过它的组件    }};
  2. 假设Activity A中有个按钮,点击之后就用隐式调用的方式调用bindService;还有个按钮B,点击之后就调用unbindService

可以看到,通过AIDL进行远程调用与不使用远程调用基本一样,只是它们产生和获取Binder的方式不同,

  1. 远程调用是通过AIDL产生Binder
  2. 非远程调用是通过继承Binder类产生Binder

4.2 Messenger实现进程间调用

Messenger方式是一种进程间消息传递到方式,可以让组件B发送消息M到Service,让Service根据消息的类型进行相关的操作,

  1. 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)    {        ......    }    ......}
  2. 创建一个用来传递消息的Messenger,将Messenger作为Binder返回给调用者,今后调用者就可以用这个给Service发消息了。消息的内容在IncomingHandlerhandleMessage()函数中得到。

    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那边在使用的时候可以,

  1. 创建一个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遇到异常情况退出时,会通过这里通知已经绑定过它的组件    }};
  2. 假设Activity A中有个按钮,点击之后就用隐式调用的方式调用bindService;还有个按钮B,点击之后就调用unbindService

这样一来,组件A就可以向Service发送消息了。Service一收到消息,就会根据消息的类型,去执行对应的操作了。

可以看出,

  1. 因为对Service操作的请求是通过Handler进行的,所以组件们请求都会按照先来后到一个一个顺序执行;
  2. 只有其它组件可以向Service发送执行某个操作的消息,而Service无法主动回报数据。

4.3 AIDL与Messenger怎么选

使用Messenger要比使用AIDL更简单,Messenger 会将所有调用排入队列,按照顺序一个一个执行;而AIDL方式允许多个组件同时向Service发送请求,所以Service需要考虑同步的问题。

对于大多数应用,Service不需要执行多线程处理,不需要数据的主动回报,因此使用Messenger可让服务一次处理一个调用;否则就使用AIDL方式吧。


/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。

*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。

*最后再次感谢各位读者对安豆的支持,谢谢:)
/*******************************************************************/

1 0
原创粉丝点击