1、AIDL:Android Interface Definition Language,即Android接口定义语言。
Android使用AIDL来支持Service和应用程序组件之间的进程间通信(IPC),包括运行在不同应用程序或者单独进程中的组件。使得Service具有跨进程便捷来支持多个应用程序的能力。
在进程间传递对象,需要将数据解析为OS级别的原语,这里通过实现Parcelable接口来实现。()
2、建立AIDL的步骤:
(1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
(2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
(3)建立一个服务类(Service的子类)。
(4)实现由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
3、AIDL服务支持的数据类型:
1)Java简单类型(int、char、boolean等)。不需要import。
2)String和CharSequence;不需要import
3)List和Map。但List和Map对象的元素类型必须是AIDL服务支持的数据类型。不需要import
4)AIDL自动生成的接口。需要import
5)实现android.os.Parcelable接口的类。需要import
下面根据工程来解析:
分别为客户端和服务端新建一个eclipse工程,实现了从客户端向服务端发送请求。
源码结构图:
1、传递的数据类类型定义
Parcelable接口
Person类对象源码:
package com.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
// 自定义的类型具体包含的数据,本例为age和name。
private intage = 0;
private String name = null;
// 这个构造函数,是方便我们在client中创新相关对象,并将之作为接口连接中调用方法的的参数
public Person()
{
}
/*提供构造函数,用于从Parcel中创建对象,也即是读的过程。这里设置为private,禁止外部调用 */
private Person(Parcel in)
{
readFromParcel(in);
}
@Override
public int describeContents()
{
return 0;
}
/*
* 【2】要实现Parcelable接口,需要实现writeToParcel()和readFromParcel(),实现将对象(数据)写入Parcel,
* 和从Parcel中读出对象
* 。需要注意,写的顺序和读的顺序必须一致,因为Parcel类是快速serialization和deserialization机制
* ,和bundle不同,没有索引机制,是线性的数据存贮和读取。
* 注意其中readFromParcel()并不是overrider,而是我们自己提供的方法,如果我们不提供,就要在 private
* Person(Parcel in){ age = in.readInt(); name = in.readString(); }
* 鉴于实际的数据类型会比小例子复杂,以及便于代码阅读,我们仿照writeToParcel()的命名,给出readFromParcel()
*/
@Override
public void writeToParcel(Parcel out, int flag)
{
out.writeInt(age); // 先写入age
out.writeString(name); // 其次写如name
}
public void readFromParcel(Parcel in)
{
age = in.readInt(); // 先读出age,保持与写同顺序
name = in.readString(); // 其次读出name,保持与写同顺序
}
/*
* 实现Parcelable接口的类必须要有一个static
* field称为CREATOR,用于实现Parcelable.Creator接口的对象
* 。在AIDL文件自动生成的Java接口中,IBinder将调用Parcelable.Creator来获得传递对象:_arg1 =
* cn.wei.flowingflying
* .proandroidservice.Person.CREATOR.createFromParcel(data);
*/
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
/* 一系列getter,setter方法 */
publicint getAge()
{
return age;
}
public String getName()
{
return name;
}
publicvoid setAge(intage)
{
this.age = age;
}
publicvoid setName(String name)
{
this.name = name;
}
}
public void writeToParcel(Parcel out, int flag)来将对象的属性保存到传递出去的Parcel对象。此外还必须实现一个公共静态的Creator域:Parcelable.Creator<Person> CREATOR,用于创建Person对象。
2、AIDL服务需要建立AIDL文件。在客户端和服务器端建立相同的aidl文件,并注意包名和文件名要完全一致。
自定义的类要注意在aidl文件中import.
Person.aidl源码:
package com.aidl;
parcelable Person;
IMyService.aidl源码:
package com.aidl;
import com.aidl.Person;
interface IMyService
{
Map getPersonAll(in String tag, in Person person);
Person getPerson();
}
类似于定义interface,可以再次提供相应的属性和方法。方法也可以接受零到多个参数。参数需要使用(in,out,inout)来修饰。
3、建立aidl文件后,Eclipse会自动生成com.aidl.IMyService.java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: F:\\Android Workspace\\AIDLServer\\src\\com\\aidl\\IMyService.aidl
*/
package com.aidl;
public interface IMyService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.aidl.IMyService {
private static final java.lang.String DESCRIPTOR = "com.aidl.IMyService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.aidl.IMyService interface,
* generating a proxy if needed.
*/
public static com.aidl.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.IMyService))) {
return ((com.aidl.IMyService) iin);
}
return new com.aidl.IMyService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@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_getPersonAll: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
com.aidl.Person _arg1;
if ((0 != data.readInt())) {
_arg1 = com.aidl.Person.CREATOR.createFromParcel(data);
} else {
_arg1 = null;
}
java.util.Map _result = this.getPersonAll(_arg0, _arg1);
reply.writeNoException();
reply.writeMap(_result);
return true;
}
case TRANSACTION_getPerson: {
data.enforceInterface(DESCRIPTOR);
com.aidl.Person _result = this.getPerson();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.aidl.IMyService {
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;
}
@Override
public java.util.Map getPersonAll(java.lang.String tag,
com.aidl.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.Map _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(tag);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getPersonAll, _data,
_reply, 0);
_reply.readException();
java.lang.ClassLoader cl = (java.lang.ClassLoader) this
.getClass().getClassLoader();
_result = _reply.readHashMap(cl);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.aidl.Person getPerson()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.aidl.Person _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply,
0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.aidl.Person.CREATOR
.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final intTRANSACTION_getPersonAll = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final intTRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.Map getPersonAll(java.lang.String tag,
com.aidl.Person person) throws android.os.RemoteException;
public com.aidl.Person getPerson() throws android.os.RemoteException;
}
注意源码中实现了stub类:抽象类,继承了Binder
public static abstract class Stub extends android.os.Binder implements com.aidl.IMyService
4、接下来实现服务器端的service:
package com.aidl;
public class MyService extends Service{
/*Service扩展Stub并实现要求的功能,相当于implements生成的IMyService.java*/
public class MyServiceImpl extends IMyService.Stub
{
@Override
public Map getPersonAll(String tag, Person person) throws RemoteException {
Map<String, String> map = new HashMap<String, String>();
map.put("tag", tag);
map.put("name", person.getName());
map.put("age", String.valueOf(person.getAge()));
map.put("person", person.toString());
return map;
}
@Override
public Person getPerson() throws RemoteException {
return new Person();
}
}
@Override
public IBinder onBind(Intent intent) {
return new MyServiceImpl();
}
}
在mainfest文件中注册service:
<service
android:name="com.aidl.MyService"
android:process=":remote">
<intent-filter>
<!-- 指定客户端调用AIDL服务所需要的Action -->
<action android:name="com.aidl.action.IMyService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
5、在客户端的Activity中绑定使用Service
package com.example.aidlclient;
import com.aidl.IMyService;
import com.aidl.Person;
public class MainActivity extends Activity {
private Button mybu;
private final static String ACTION_TAG = "com.aidl.action.IMyService";
//MyService可以当做普通的Service形式进行调用
private IMyService iMyService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获得service的实例
iMyService = IMyService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mybu = (Button) findViewById(R.id.mybu);
//绑定Service
bindService(new Intent(ACTION_TAG), serviceConnection, Context.BIND_AUTO_CREATE);
mybu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
doWithService();
} catch (RemoteException e) {
}
}
});
}
/*******操作Service提供的服务
* @throws RemoteException ************/
private void doWithService() throws RemoteException
{
Person person = iMyService.getPerson();
person.setAge(10);
person.setName("Mary");
Log.i("doWithService", "getName :" + person.getName());
Log.i("doWithService", "getAge :" + String.valueOf(person.getAge()));
Log.i("doWithService", "getPersonAll :" + iMyService.getPersonAll("Map", person));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
intid = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
使用bindService(new Intent(ACTION_TAG), serviceConnection, Context.BIND_AUTO_CREATE);来对Service进行绑定。
在onServiceConnected中通过iMyService = IMyService.Stub.asInterface(service);来获取实例;进而通过Service调用相关服务。
客户端在执行bindService的时候,成功绑定服务之后,会回调mConnection的onServiceConnected(),并且传回了服务端的通信接口IBinder,此IBinder即服务onBind()时返回的IBinder,详见mAIDLService.java。
在onServiceConnected(),客户端成功获取了服务端通信接口,实际上是本地代理对象,该对象存在于客户端进程空间,客户端只和代理对象交互,真正的IPC通信是本地代理对象和服务端的通信。
注意:bindService是异步实现的,绑定需要一定的时间,不可bindService之后立即执行iMyService的相关操作,因为可能为来得及绑定,导致iMyService仍为空。
整个交互流程如下:
1.客户端通过绑定服务,获取了服务的句柄(本地代理对象);
2.客户端执行onClick(),调用本地代理对象的get()等函数,本地代理对象调用mRemote.transact()发出远程调用请求;
3.服务端响应onTransact()执行this.get(),并将执行结果返回;
由于客户端只和本地代理对象即服务句柄通信,由代理对象进行真正的IPC操作,所以对客户端来说,IPC过程是透明的,调用远程操作如同调用本地操作一样。在客户端调用transact()时,会将服务描述DSCRIPTION写入到data里,在客户端onTransact时会验证,如果两个不一样,则不能通信。而DSCRIPTION是根据mInterface包名和接口名自动生成的,这就是为什么两个工程里的aidl文件要在同一个包的原因。
在这个过程中,aidl起到了桥梁的作用,规定统一了客户端和服务端的通信接口,使得客户端和服务端得以成功的通信。
具体的通信transact和onTransact的过程也就是利用Binder驱动通信的过程。