AIDL跨进程通讯访问远程服务

转载注明出处:http://blog.csdn.net/u012861467/article/details/51684484

AIdl 中文意思是Android 接口定义语言,用于跨进程通讯定义接口用的。跨进程通讯在android中用得最多的就是Binder了,而AIDL就是配套Binder使用的一种接口定义语言,它会转化生成对应的接口文件。

Binder跨进程通讯传递的数据类型是有限制的,如下:
1、JAVA的基本数据类型(int, long, char, boolean等)。不需要import包。
2、String 和 CharSequence。不需要import包。
3、集合类型 java.util.List和java.util.Map,不需要import包。但是集合里面的项的数据类型同样要满足这几个限制,AIDL接口和 实现Parcelable的复杂类都要import包。
4、AIDL 接口,需要import包,即使在同一个包下。
5、实现 android.os.Parcelable 接口的复杂类。需要import包。

注意:AIDL定义文件中方法的参数除了基本数据类型、String类型和CharSequence类型之外,其它类型都需要使用方向标签,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
例如:
String showPerson(in Person person);

下面带大家来使用一下AIDL进行跨进程通讯,这个例子的功能是创建一个远程服务,使用客户端调用服务中的方法。传递的数据有简单的数据类型也有复杂的数据类型。
下面接着开始罗,一步一步来!!!

步骤一:先定义AIDL文件

如果使用的开发工具是Android studio,那么点击选中新建工程的根目录右键--> NEW--> AIDL--> AIDL File,填写自己的aidl文件名称,点击“OK”,工具会自动帮你生成一个aidl后缀的文件,该文件是放在跟java文件夹同级的aidl文件夹下,路径跟工程的包名一致。
代码如下:
package com.example.aidlservicetest;
import com.example.aidlservicetest.Person;

interface IMyServiceInterface {
      
      // 自己定义的一些方法声明
      int add(int a,int b);

      // 实现Parcelable 接口的复杂类需要import包
      String showPerson(in Person person);

      List<Person> getPersonList(in Person person);

      Map getMap(String key, in Person person);

      Person getPerson();
}
把里面生成的多余的代码删掉,添加自己的一些方法声明,可以注意到不同的数据类型的写法应该遵循上面列出的限制。
定义aidl文件完成后,点击菜单栏的“Build”-->Make Project,开发工具会生成aidl文件对应的java接口文件,我们不需要去编辑它。


上面的 Person类实现了Parcelable接口。

下面补充一下实现Parcelable接口注意的地方:

先定义一个Person类实现Parcelable接口,生成的代码如下:
public class Person implements Parcelable {
    protected Person(Parcel in) {
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
    }
}
Parcelable需要实现两个方法和一个接口:
1) void writeToParcel(Parcel dest, int flags) 把数据放到Parcel对象里面。读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
2) describeContents() 通常直接返回0
3) 定义一个static final 修饰的实现android.os.Parcelable.Creator<T>接口的Creator类对象CREATOR,它的名字是固定的。
实现接口的两个方法:
   createFromParcel(Parcel source) 从Parcel对象中生成实例对象。读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
   newArray(int size) 创建长度为size的实例对象数组。

下面是完整的Person类代码:
package com.example.aidlservicetest;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable{
    private String name;//名字
    private int sex;//性别

    public Person(){}

    //从Parcel解析出Person
    protected Person(Parcel in) {
        readFromParcel(in);
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        //注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果
        parcel.writeString(name);
        parcel.writeInt(sex);
    }

    //注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果
    public void readFromParcel(Parcel source) {
        name = source.readString();
        sex = source.readInt();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "name=" + name +", sex=" + sex;
    }
}

因为自定义的Person类是复杂数据类型,在同一包下还需要新建一个对应的Person.aidl文件告诉系统我们需要序列化和反序列化的类型。每一个实现了Parcelable接口的复杂类都需要对应的.aidl文件。AIDL编译器在编译AIDL文件时会自动查找此类文件。

文件目录结构:


Person.aidl 内容如下:

package com.example.aidlservicetest;
parcelable Person;

只是声明了Person 类需要序列化和反序列化。

步骤二:创建远程服务

创建一个类继承Service类,例子里定义的是MyService类。
AndroidManifest.xml文件中MyService类声明:
<service
     android:name=".MyService"
     android:enabled="true"
     android:exported="true"
     android:process=":MyService">
     <intent-filter>
          <action android:name="com.example.aidlservicetest.myservice"/>
     </intent-filter>
</service>
增加一个自定义action,可以让客户端使用隐式Intent绑定服务。

要让自定义的Service成为远程服务,在AndroidManifest.xml文件中为Service的声明添加android:process属性。因为Service默认的是运行在跟应用同一个进程里,如果要想Service运行在独立的进程可以添加android:process属性。该属性同样适用于其它Android组件(例如Activity, Service,Application等)。

android:process属性的写法有两种:
1)android:process=":自定义名称"
加冒号表示该进程是本应用的私有进程,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。进程名为:本应用的标准进程名:自定义名称
例如:
本应用的标准进程名:com.example.servicetest
android:process=":myThread"
另起进程名:com.example.servicetest:myThread

2)android:process="自定义名称"
如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。进程名为:自定义名称

步骤三:实现aidl文件定义的方法

aidl文件生成的java接口文件中已经创建了一个Stub类,Stub类继承了android.os.Binder类,实现了我们定义的接口。我们在Service中自定义一个类继承Stub类,实现aidl文件中声明的几个方法。
代码如下:
class ServiceBinder extends IMyServiceInterface.Stub{

        @Override
        public int add(int a, int b) throws RemoteException {
            return a+b;
        }

        @Override
        public String showPerson(Person person) throws RemoteException {
            return person.toString();
        }

        @Override
        public List<Person> getPersonList(Person person) throws RemoteException {
            List<Person> personList = new ArrayList<>();
            personList.add(person);
            return personList;
        }

        @Override
        public Map getMap(String key, Person person) throws RemoteException {
            Map map = new HashMap();
            map.put(key,key);
            map.put("name",person.getName());
            map.put("sex",person.getSex());
            return map;
        }

        @Override
        public Person getPerson() throws RemoteException {
            Person person = new Person();
            person.setName("Tom");
            person.setSex(0);
            return person;
        }
}
我们自定义的类ServiceBinder继承了Stub类,所以可以在Service的onBind(Intent intent)方法中作为参数返回。
@Override
public IBinder onBind(Intent intent) {
   Log.i(Tag,"onBind");
   return new ServiceBinder();
}

步骤四:绑定远程服务,调用服务的方法

在Activity中绑定远程服务
Intent intent = new Intent("com.example.aidlservicetest.myservice");
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
bindService方法里面的参数serviceConnection是一个实现 ServiceConnection接口的对象,我们可以这样创建它:
serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                serviceBinder = IMyServiceInterface.Stub.asInterface(iBinder);
                try {
                    Person p = serviceBinder.getPerson();
                    Log.i(Tag,"getPerson:"+p.toString());

                    int res = serviceBinder.add(2,3);
                    Log.i(Tag,"add(2,3):"+res);

                    Person pp = new Person();
                    pp.setName("JOHN");
                    pp.setSex(0);

                    Map mm = serviceBinder.getMap("Key",pp);
                    Log.i(Tag,"getMap(\"Key\",pp):"+mm);

                    String str = serviceBinder.showPerson(pp);
                    Log.i(Tag,"showPerson:"+str);

                    List<Person> list = serviceBinder.getPersonList(pp);
                    Log.i(Tag,"getPersonList:"+list);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.i(Tag,"onServiceDisconnected:unbind");
            }
};

当Service绑定之后会回调 onServiceConnected 方法,传入的参数有一个IBinder 对象,通过IMyAidlInterface.Stub.asInterface(iBinder); 可以获取远程服务的Binder对象。有了这个Binder对象就可以调用远程服务的方法了。

输出的结果为:

转载注明出处:http://blog.csdn.net/u012861467/article/details/51684484

源码地址

服务器端:https://github.com/StarsAaron/AIDLServiceTest.git

客户端:https://github.com/StarsAaron/AIDLClientTest.git

这是两个不同的应用,在服务器端也可以绑定远程服务,,当服务器端和客户端同时绑定了远程服务,这时候有两个Activity绑定了远程服务,当其中的一个解绑了,远程服务还是没有销毁的,直到两个绑定都解绑了才会调用onDestroy销毁。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星点点-

请我喝杯咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值