1.什么是AIDL
IPC(Inter Process Communication)是Android为了能让进程间进行通讯而提出来的,其中AIDL(Android Interface Definition Language,Android接口定义语言)是IPC的一个轻量级的实现,编译器通过*.aidl文件生成对应的Java代码,供我们调用。
2.例子
2.1 需要了解的:如果对于bindService还不够熟悉,先了解一下会比较好。
2.2 例子简介:2个进程,一个服务端,一个客户端,服务端初始化一些消息,客户端跨进程拿到服务端的信息,进而显示。
2.3 服务端:
IMyService.aidl
import com.cxy.aidl.Person;
interface IMyService {
List<Person> getPersons();
void addPerson(in Person person);
}
在这里需要注意一下,AIDL的传输规则只允许基本的数据类型,而其他自定义class都必须显式import,即使是本包下的class,且必须标识其输入输出类型,这里用in标记,表示输入型参数。如果你使用的IDE是Eclipse,且开启了自动编译,可以看到在项目的gen目录下自动生成了一个代理类:
public static abstract class Stub extends android.os.Binder implements com.cxy.aidl.IMyService
大家可以看下自己的程序是否成功生成。
Person.aidl
package com.cxy.aidl;
parcelable Person;
注意:parcelable 是小写,Person大写。
public final class Person implements Parcelable {
public String name;
public int sex;
public int age;
public Person() {
}
public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person[] newArray(int size) {
// TODO Auto-generated method stub
return new Person[size];
}
@Override
public Person createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new Person(source);
}
};
public Person(Parcel in) {
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
name = in.readString();
sex = in.readInt();
age = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(sex);
dest.writeInt(age);
}
@Override
public String toString() {
return String.format(Locale.ENGLISH, "Studeng[ %s, %d, %d ]\n", name, sex, age);
}
}
android sdk中提供了轻量级的序列化方法,非基本类型的对象在AIDL中传输需要被序列化,即
Parcelable
接口。
MyService.java
public class MyService extends Service {
private final String TAG = MyService.class.getSimpleName();
private static final String ACCESS_PACKAGE_NAME = "com.example.aidlclient";
private boolean mCanRun = true;
private List<Person> mPersons = new ArrayList<Person>();
private final IMyService.Stub mBinder = new Stub() {
// 在这里可以做权限认证,只允许包名为com.example.aidlclient的客户端通过,return false意味着客户端的调用会失败,比如下面,
// 其他应用将无法完成调用过程
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws RemoteException {
String packageName = null;
String[] packages = MyService.this.getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact:" + packageName);
if (!ACCESS_PACKAGE_NAME.equals(packageName)) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public List<Person> getPersons() throws RemoteException {
synchronized (mPersons) {
return mPersons;
}
}
@Override
public void addPerson(Person person) throws RemoteException {
synchronized (mPersons) {
if (!mPersons.contains(person))
mPersons.add(person);
}
};
};
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
Thread thread = new Thread(null, new ServiceRunnable(), "BackgroundService");
thread.start();
synchronized (mPersons) {
for (int i = 1; i < 3; i++) {
Person student = new Person();
student.name = "student#" + i;
student.age = i * 15;
mPersons.add(student);
}
}
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, String.format("onBind, intent = %s", intent.toString()));
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
mCanRun = false;
super.onDestroy();
}
class ServiceRunnable implements Runnable {
int counter = 0;
@Override
public void run() {
// do background processing here.....
while(mCanRun) {
Log.d(TAG, "counter:"+counter);
counter ++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
这里创建了服务端的Service,初始化了mPersons信息,通过mBinder输出信息,然后在onBind中返回当前binder对象。在
onTransact这里做了权限控制,具体在代码中已有注释。
AndroidManifest.xml
<service
android:name="com.cxy.main.MyService"
android:exported="true"
android:process=":remote" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.cxy.main.MyService" />
</intent-filter>
</service>
由此已经实现了服务端的程序,但它是“死”的,单纯提供了数据,真正对数据进行处理的是客户端,接下来,new 一个新的工程:
客户端也需要在相同的报下创建同名的aidl文件,在这里把服务端程序下的com.cxy.aidl包整个拷贝至客户端,也就是说,服务端和客户端的程序是一致的。如不一直,则程序会crash掉。下面是客户端的MainActivity.java代码,主要区别在这里。
MainActivity.java
public class MainActivity extends Activity {
private final String ACTION_BIND_SERVICE = "com.cxy.main.MyService";
private IMyService mIMyService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 通过服务端onBind方法返回的binder对象得到IMyService的实例,得到实例就可以调用他的方法了
mIMyService = IMyService.Stub.asInterface(service);
try {
Person person = mIMyService.getPersons().get(0);
showDialog(person.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
public void showDialog(String message) {
new AlertDialog.Builder(MainActivity.this)
.setTitle("cxy")
.setMessage(message)
.setPositiveButton("确定", null)
.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 开启访问服务端Service
Intent intentService = new Intent(ACTION_BIND_SERVICE);
intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
if (mIMyService != null) {
unbindService(mServiceConnection);
}
super.onDestroy();
}
}