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

更新时间修改意见
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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值