AIDL的作用
AIDL(Android Interface Definition Language)它是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL实现。
AIDL的使用场合
官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。
下面通过一个例子来实现AIDL,这个例子实现效果:通过点击客户端的按钮随机查询支付的信息,并返回结果数据给客户端。
服务端的工程目录
服务端的代码如下:
1、AliPayService.java的代码
public class AliPayService extends Service {
//创建一个用于保存PayClass对象信息的HashMap对象
private Map<String, PayClass> mPayClass = new HashMap<String, PayClass>();
private String names[];
private String payTypes[];
private int payMoneys[];
/**
* 模拟数据
*/
private void init(){
names = new String[]{"mike", "lisa", "rose", "jhon", "liz", "seven"};
payTypes=new String[]{"支付宝","工商银行","建设银行","中国银行","交通银行","农业银行"};
payMoneys=new int[]{120,230,180,200,70,140};
for (int i = 0; i < names.length; i++){
mPayClass.put(names[i], new PayClass(names[i], payTypes[i], payMoneys[i]));
}
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("远程支付服务: onBind");
return new MyBinder();
}
private PayClass methodInService(){
System.out.println("调用远程支付服务中的方法......");
Random random=new Random();
int i=random.nextInt(6);
return mPayClass.get(names[i]);
}
private class MyBinder extends IService.Stub{
@Override
public PayClass callMethodInService() {
return methodInService();
}
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("远程支付服务: onUnbind");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
System.out.println("远程支付服务: onCreate");
super.onCreate();
init();
}
@Override
public void onDestroy() {
System.out.println("远程支付服务: onDestroy");
super.onDestroy();
}
}
既然是一个服务,就要记得在AndroidManifest.xml文件中添加服务并添加相应的action:
<service android:name="com.myself.alipayservice.AliPayService">
<intent-filter>
<action android:name="com.myself.alipay"/>
</intent-filter>
</service>
2、MainActivity.java
这个文件不用修改,只要使用工程默认生成的就可以。
3、PayClass.java
这个类必须实现Parcelable接口。
public class PayClass implements Parcelable {
private String name;
private String payType;
private int payMoney;
public PayClass(String name,String payType, int payMoney) {
this.name=name;
this.payType = payType;
this.payMoney = payMoney;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPayType() {
return payType;
}
public void setPayType(String payType) {
this.payType = payType;
}
public int getPayMoney() {
return payMoney;
}
public void setPayMoney(int payMoney) {
this.payMoney = payMoney;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
/*将PayClass的成员写入Parcel,
* 注:Parcel中的数据是按顺序写入和读取的,即先被写入的就会先被读取出来
*/
dest.writeString(name);
dest.writeString(payType);
dest.writeInt(payMoney);
}
//该静态域是必须要有的,而且名字必须是CREATOR,否则会出错
public static final Parcelable.Creator<PayClass> CREATOR=new Creator<PayClass>() {
@Override
public PayClass[] newArray(int size) {
return new PayClass[size];
}
@Override
public PayClass createFromParcel(Parcel source) {
//从Parcel读取通过writeToParcel方法写入的PayClass的相关信息
String n=source.readString();
String pt=source.readString();
int pm=source.readInt();
return new PayClass(n,pt, pm);
}
};
}
这里使用Parcelable 接口来序列化,是Android提供的一个比Serializable 效率更高的序列化类。
Parcelable需要实现三个函数:
1) void writeToParcel(Parcel dest, int flags) 将需要序列化存储的数据写入外部提供的Parcel对象dest。要注意的是,读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。
2) describeContents() 没搞懂有什么用,反正直接返回0也可以。
3) static final Parcelable.Creator对象CREATOR 这个CREATOR命名是固定的,而它对应的接口有两个方法:
createFromParcel(Parcel source) 实现从source创建出JavaBean实例的功能
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。
4、PayClass.aidl文件,由于PayClass.java文件是一个自定义的实体类,所以要再新建一个名称为PayClass.aidl的文件来声明这个自定义的数据类型,内容如下:
package com.myself.alipayservice;
parcelable PayClass;
关键字package声明PayClass.java类在哪个包中,parcelable是在系统中注册这个类名称。
5、IService.aidl
内容如下:
package com.myself.alipayservice;
import com.myself.alipayservice.PayClass;
interface IService {
PayClass callMethodInService();
}
创建完IService.aidl后,工程会自动在gen下对应的包中生成IService.java文件
打开文件后发现有这么一段代码:
public static abstract class Stub extends android.os.Binder implements com.myself.alipayservice.IService
说明这个抽象类Stub不仅继承了Binder还实现了IService接口。
创建aidl文件时要注意以下:
AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。
而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import.
需要特别注意的是, 对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
客户端的代码完成后记得要部署到模拟器上,刚开始时没有部署到模拟器就运行就会报错:没有找到服务。
客户端的目录结构
从上面可以看出,客户端中的com.myself.alipayservice包下的文件和服务端是一样的,除了少了MainActivity.java和AlipayService.java文件。
没错,这些文件都是直接从服务端拷贝到客户端的,要保证包名和文件内容一致。
客户端的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<Button
android:onClick="bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="绑定远程服务" />
<Button
android:onClick="call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="调用远程服务方法"/>
</LinearLayout>
MainActivity.java 文件代码如下:
public class MainActivity extends Activity {
private PayClass pc;
private Intent intent;
private Myconn conn;
private IService iService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent=new Intent();
/**
* 跟服务端定义的action一致
*/
intent.setAction("com.myself.alipay");
conn=new Myconn();
}
public void bind(View view){
bindService(intent, conn, BIND_AUTO_CREATE);
}
private class Myconn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 注意这里不是用(IService)强制类型转换,而是直接调用asInterface()
*/
iService=IService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
public void call(View view){
try {
pc=iService.callMethodInService();
Toast.makeText(this, "支付信息如下:\n 姓名:"+pc.getName()+"支付方式:"+pc.getPayType()+"支付金额:"+pc.getPayMoney(), 1).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 在退出应用时解绑服务
*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
执行效果如下图
观察logcat日志,如下图
总结以下调用远程服务的主要步骤:
1、在客户端的activity中调用bindService()去绑定服务,bindService(intent, conn, BIND_AUTO_CREATE),在这个过程中需要传递一个叫ServiceConnection的接口参数,因此
要定义一个Myconn去实现这个接口,实现这个接口会返回两个回调方法public void onServiceConnected(ComponentName name, IBinder service) 和
public void onServiceDisconnected(ComponentName name)。
2、在服务里需要重写onBind()方法,并返回一个IBinder接口。返回的IBinder接口可以通过定义一个类去继承IService.Stub(定义的接口,并将后缀名改成.aidl),在类中调用
服务的方法;定义对应的javabean并实现parcelable接口。
3、在客户端的onServiceConnected(ComponentName name, IBinder service)得到IBinder,并将IBinder转换成IService,然后即可用IService调用服务中的方法。
源代码下载:http://download.csdn.net/detail/dangnianmingyue_gg/9076753