AIDL 学习

2015/7/4 posted in  Android

AIDL的作用

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

选择AIDL的使用场合

官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。

如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。

如何使用AIDL

先建立一个android工程,用作服务端

创建一个android工程,用来充当跨进程通信的服务端。

2.创建一个包名用来存放aidl文件

创建一个包名用来存放aidl文件,在里面新建IMyService.aidl文件,如果需要访问自定义对象,还需要建立对象的aidl文件,这里我们由于使用了自定义对象Student,所以,还需要创建Student.aidl和Student.java。注意,这三个文件,需要都放在aidl文件夹下com.example.xuyushi.aidltest包里。下面描述如何写这三个文件。

在android studio中如何编译.aidl文件生成.java文件(重要)

  1. 在app->src->main目录下新建aidl文件夹,注意 在android工程下不显示,需要切换到Project。
  2. 在aidl新建package。包名需要与AndroidManifest.xml中的包名一致
  3. 在 新建.aidl文件,注意打包的报名与上面一致

    package com.example.xuyushi.aidltest;
    interface IMyService{
    String getValue();
    }
  4. 在app->build->generated->source->aidl。中会生成IMyService.java

IMyService.aidl如下

package com.example.xuyushi.aidltest;
import com.example.xuyushi.aidltest.Student;  

interface IMyService {

    List<Student> getStudent();
    void addStudent(in Student student);
}

aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里,比如上面的Student,尽管它和IMyService在同一个包中,但是还是需要显示的import进来。
另外,接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示,上面的代码我们用in标记,因为它是输入型参数。
在gen下面可以看到,eclipse为我们自动生成了一个代理类
public static abstract class Stub extends android.os.Binder implements com.ryg.sayhi.aidl.IMyService
可见这个Stub类就是一个普通的Binder,只不过它实现了我们定义的aidl接口。它还有一个静态方法
public static com.ryg.sayhi.aidl.IMyService asInterface(android.os.IBinder obj)
这个方法很有用,通过它,我们就可以在客户端中得到IMyService的实例,进而通过实例来调用其方法。

Student.aidl代码

package com.example.xuyushi.aidltest;

parcelabl

这里parcelable是个类型,首字母是小写的,和Parcelable接口不是一个东西,要注意。

Student.java

Student.java不要建在aidl文件夹中,在com.example.xuyushi.aidltest和MainActivity一起即可

package com.example.xuyushi.aidltest;

/**
 * Created by xuyushi on 15/7/4.
 */
import java.util.Locale;

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

public final class Student implements Parcelable {

    public static final int SEX_MALE = 1;
    public static final int SEX_FEMALE = 2;

    public int sno;
    public String name;
    public int sex;
    public int age;

    public Student() {
    }

    public static final Parcelable.Creator<Student> CREATOR = new
            Parcelable.Creator<Student>() {

                public Student createFromParcel(Parcel in) {
                    return new Student(in);
                }

                public Student[] newArray(int size) {
                    return new Student[size];
                }

            };

    private Student(Parcel in) {
        readFromParcel(in);
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(sno);
        dest.writeString(name);
        dest.writeInt(sex);
        dest.writeInt(age);
    }

    public void readFromParcel(Parcel in) {
        sno = in.readInt();
        name = in.readString();
        sex = in.readInt();
        age = in.readInt();
    }

    @Override
    public String toString() {
        return String.format(Locale.ENGLISH, "Student[ %d, %s, %d, %d ]", sno, name, sex, age);
    }

}  

创建服务端service

创建一个service,比如名为MyService.java


说明:通过AIDL传输非基本类型的对象,被传输的对象需要序列化,序列化功能java有提供,但是android sdk提供了更轻量级更方便的方法,即实现Parcelable接口,关于android的序列化,我会在以后写文章介绍。这里只要简单理解一下就行,大意是要实现如下函数
readFromParcel : 从parcel中读取对象
writeToParcel :将对象写入parcel
describeContents:返回0即可
Parcelable.Creator CREATOR:这个照着上面的代码抄就可以
需要注意的是,readFromParcel和writeToParcel操作数据成员的顺序要一致

在AndroidMenifest中声明service

<service  
    android:name="com.ryg.sayhi.MyService"  
    android:process=":remote"  
    android:exported="true" >  
    <intent-filter>  
        <category android:name="android.intent.category.DEFAULT" />  
        <action android:name="com.ryg.sayhi.MyService" />  
    </intent-filter>  
</service>  

说明:上述的 是为了能让其他apk隐式bindService,通过隐式调用的方式来起activity或者service,需要把category设为default,这是因为,隐式调用的时候,intent中的category默认会被设置为default。

待续

参考

http://android.blog.51cto.com/268543/537684/
http://blog.csdn.net/singwhatiwanna/article/details/17041691
http://developer.android.com/guide/components/aidl.html