Android Service 远程服务

来源:互联网 发布:oss 阿里云 编辑:程序博客网 时间:2024/05/16 06:00

远程服务的创建和调用需要使用AIDL语言,步骤如下:

  1. 使用AIDL语言定义远程服务的接口

  2. 通过继承Service类实现接口中定义的方法和属性

  3. 绑定和使用远程服务

以下为一个简单Demo ,RemoteMathCallerDemo界面如下:
这里写图片描述

绑定远程服务后,调用RemoteMathServiceDemo中的MathService服务进行加法运算。

1.使用AIDL语言定义远程服务的接口

以Android Studio为例,首先需要建立对应目录及aidl文件,如下:
这里写图片描述

(比如直接在java目录下的包上右键新建aidl文件 IDE会自动生成aidl目录及该目录下的包和文件这样的小技巧我可不会随便告诉别人)
IMathService.aidl文件内容如下:

// IMathService.aidlpackage com.example.remotemathservicedemo;// Declare any non-default types here with import statementsinterface IMathService {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */     long Add(long a,long b);}

然后在build目录下会自动生成与该aidl文件对应的java接口文件,(若没有生成则重新make project) 如下:
这里写图片描述

在看IMathService.java内容之前呢,不知道你有没有注意到,我前两张截图都截到了上面的一个Module:remotemathcallerdemo,这个就是调用端,目前我们编辑的remotemathservicedemo是服务端。

下面为IMathService.java的完整代码,加上了我自己的理解和注释:

/* * 这个文件是自动生成的。不要修改 */package com.example.remotemathservicedemo;/*   在这里声明任何非默认类型     所有使用AIDL建立的接口都必须继承 android.os.IInterface 基类接口     这个基类接口中定义了 asBinder()方法 用来获取Binder对象 */public interface IMathService extends android.os.IInterface {    /**     * 本地IPC实现stub类     */    public static abstract class Stub extends android.os.Binder implements com.example.remotemathservicedemo.IMathService {        private static final java.lang.String DESCRIPTOR = "com.example.remotemathservicedemo.IMathService";        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        //asInterface(IBinder) 是Stub内部的远程服务接口,调用者可以通过该方法获得远程服务的实例        public static com.example.remotemathservicedemo.IMathService asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            //判断android.os.IInterface实例是否为本地服务 若是返回android.os.IInterface            //若不是本地服务 构造Proxy对象并返回之            if (((iin != null) && (iin instanceof com.example.remotemathservicedemo.IMathService))) {                return ((com.example.remotemathservicedemo.IMathService) iin);            }            return new com.example.remotemathservicedemo.IMathService.Stub.Proxy(obj);        }        //实现了android.os.IInterface接口定义的asBinder()方法        @Override        public android.os.IBinder asBinder() {            return this;        }        /*        Parcel是Android系统应用程序间传递数据的容器,能够在两个进程中完成打包和拆包的工作             但Parcel不同于通用意义上的序列化        Parcel的设计目的是用于高性能IPC传输 不能将其保存在持久存储设备上         */        //接收Parcel对象,并从中逐一读取每个参数,然后调用Service内部制定的方法,将结果写进另一个Parcel对象,        // 准备将这个Parcel对象返回给远程的调用者        @Override        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {            switch (code) {                case INTERFACE_TRANSACTION: {                    reply.writeString(DESCRIPTOR);                    return true;                }                case TRANSACTION_Add: {                    data.enforceInterface(DESCRIPTOR);                    long _arg0;                    _arg0 = data.readLong();                    long _arg1;                    _arg1 = data.readLong();                    long _result = this.Add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeLong(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        //用来实现远程服务调用        private static class Proxy implements com.example.remotemathservicedemo.IMathService {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote) {                mRemote = remote;            }            @Override            public android.os.IBinder asBinder() {                return mRemote;            }            public java.lang.String getInterfaceDescriptor() {                return DESCRIPTOR;            }            //以一定顺序将所有参数写入Parcel对象,以供Stub内部的onTransact()方法获取参数            @Override            public long Add(long a, long b) throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                long _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeLong(a);                    _data.writeLong(b);                    mRemote.transact(Stub.TRANSACTION_Add, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readLong();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_Add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    }public long Add(long a, long b) throws android.os.RemoteException;}

IMathService.aidl是对远程服务接口的定义,自动生成的IMathService.java内部实现了远程服务数据传递的相关方法,下一步介绍如何实现远程服务,这需要建立一个Service类,并在该类中通过onBind()方法返回IBinder对象,这样调用者使用获取的IBinder对象就可以访问远程服务。

下面是MathService.java的完整代码:

package com.example.remotemathservicedemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.support.annotation.Nullable;import android.widget.Toast;/** * Created by yinghao on 2016/5/7. */public class MathService extends Service {    /*           1. 建立 IMathService.Stub的实例mBinder并实现AIDL文件定义的远程服务接口           2. 在onBind()方法中将mBinder返回给远程调用者     */    private final IMathService.Stub mBinder = new IMathService.Stub(){        @Override        public long Add(long a, long b) throws RemoteException {            return a + b;        }    };    @Nullable    @Override    public IBinder onBind(Intent intent) {        Toast.makeText(MathService.this, "远程绑定:MathService", Toast.LENGTH_SHORT).show();        return mBinder;    }    //Return true if you would like to have the service's onRebind method later called when new clients bind to it.    @Override    public boolean onUnbind(Intent intent) {        Toast.makeText(MathService.this, "取消远程绑定", Toast.LENGTH_SHORT).show();        return false;    }}

最后一步,注册service:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.remotemathservicedemo">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <service android:name=".MathService"            android:process=":remote">            <intent-filter>                <action android:name="com.example.remote.MathService" />            </intent-filter>        </service>    </application></manifest>

到这里我们的服务端Module : remotemathservicedemo 就完成了。

这完成了文章开头所列举的三个步骤的前两步,最后一步,让我们来看看怎么绑定和使用远程服务吧。

首先,我们需要引入与服务端相同的aidl文件并确保自动生成对应的IMathService.java接口文件

那么为什么要这样做呢? 这就需要我们了解aidl文件和对应接口文件的用处到底是什么,为了时数据能穿越进程边界,所有数据都必须是“打包”,而自动生成的IMathService.java内部实现了远程服务数据传递的相关方法,那么服务端就有了将数据打包、拆包的能力。而调用端也需要发出数据和接收数据,也需要有将数据打包、拆包的能力,所以它也需要IMathService.java这个类。

然后对远程服务的绑定与调用,其实与本地服务的绑定区别不大,不同之处主要包括两处:

  1. 使用IMathService生命远程服务实例

  2. 通过IMathService.Stub的asInterface()方法获取服务实例

下面为remotemathcallerdemo Module中MainActivity.java的完整代码:

package com.example.remotemathcallerdemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;import com.example.remotemathservicedemo.IMathService;import org.w3c.dom.Text;public class MainActivity extends AppCompatActivity {    private TextView textView;    private Button bind;    private Button unbind;    private Button add;    private boolean isBound = false;    private IMathService mathService;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mathService = IMathService.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {            mathService = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        textView = (TextView) findViewById(R.id.textView);        bind = (Button) findViewById(R.id.bind);        unbind = (Button) findViewById(R.id.unbind);        add = (Button) findViewById(R.id.add);        bind.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (!isBound) {                    final Intent serviceIntent = new Intent();                    serviceIntent.setAction("com.example.remote.MathService");                    bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);                    isBound = true;                }            }        });        unbind.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (isBound) {                    unbindService(mConnection);                    isBound = false;                    mathService = null;                }            }        });        add.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (mathService == null) {                    textView.setText("未绑定远程服务");                    return;                }                long a = Math.round(Math.random() * 100);                long b = Math.round(Math.random() * 100);                long result = 0;                try {                    result = mathService.Add(a, b);                } catch (RemoteException e) {                    e.printStackTrace();                }                String msg = String.valueOf(a) + "+" + String.valueOf(b) + "=" + String.valueOf(result);                textView.setText(msg);            }        });    }}

在此例中传递的数据类型为基本数据类型,打包过程是自动完成的,但对于自定义的数据类型,用户则需要实现Parcelable接口,使自定义的数据类型能够转换为系统级原语保存在Parcel对象中,穿越进程边界后可再转换为初始格式,关于自定义数据类型的传递,在下一篇文章中归纳总结。

1 0
原创粉丝点击