在android中,不同的应用运行在各自的进程中,互不干扰,一个进程也不能直接的去访问另一个进程的内存空间,因此,进程间通讯,android提供了AIDL这个工具来实现。
AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDL IPC机制是面向接口的,更加轻量级。它是使用代理类在客户端和实现端传递数据。
1.定义AIDL接口
其实,AIDL接口文件和普通的接口文件没有什么区别,只不过它的扩展名是.aidl罢了。因此同其它接口是一样的,只能是接口的声明和方法的声明,它还不能有static成员变量。编辑完成之后,它会自动在gen目录文件夹下自动生成IBinder接口文件。service必须实现这个IBinder接口,那么客户端才能绑定此service然后才能从IBinder中调用方法实现不同进程间的通讯。客户端要想绑定此service并且能顺利的实现通讯,必须要完整的拷贝一份AIDL接口,包括包名以及类名和所需要的其它东东,都必须是完全相同的拷贝,不能有一点差别。(暂时我的理解就是这样)AIDL声明接口的语法很是简单,只是用来描述参数以及返回值,甚至可以是其它的AIDL接口类型。不同的参数类型引用的时候是不一样的。如java数据的基本类(int,long,char,boolean)以及string和charSquence,list,map等是不需要import导入的;如果需要使用其它的AIDL接口以及实现了Parcelable接口的类,它们即使是在同一个包中,也是需要import的。还有就是,对于非java基本数据类型,包括string和charSquence,需要加上方向的指示(in,out,inout),in表示是由客户端设置,out表示服务器端设置,inout表示两端都可以进行设置。
2.创建传递数据Bean--Student类
(1)writeToParcel(Parcel dest, int flags),将序列化存储的数据写入到外部提供的Parcel对象当中,以便读取。
(2)describeContents,网上叫内容接口描述,直接返回0即可,具体是干啥用的,反正我也不知道。
(3)static final Parcelable.Creator 对象 CREATOR,名字是固定的不可以更改。对应的两个接口分别是
createFromParcel(Parcel source):实现从source中创建出Bean实例的功能,如new Student(source)
newArray(int size):创建一个长度为size的类型为Bean的数组,如new Student[size]
(4)此外,既然数据能存了,还得必须能读出来啊,因此方法readFromParcel(Parcel in)这是可以要有的,读取
的顺序与写入的顺序是一样一样的,不能有错。Student类如下:
package com.dandy.AIDL;
import android.os.Parcel;
import android.os.Parcelable;
public 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>() {
@Override
public Student createFromParcel(Parcel source) {
return new Student(source);
}
@Override
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();
}
}
(5)实体类Student创建完成之后,需要创建Student.aidl文件,注意的是,这里的parcelable与实现的Parcelable是不一样的,
前者首字母p是小写,后者首字母P是大写,千万不要搞混咯。代码如下:
package com.dandy.AIDL;
parcelable Student;
简单得很,就一句话就完事了。
3.创建AIDL接口
新建包:com.dandy.AIDL,然后在包中新建名为IMyService.aidl和IMyServiceResult.aidl的接口。如下:
package com.dandy.AIDL;
interface IMyServiceResult{
void backResult(int result);
}
package com.dandy.AIDL;
import com.dandy.AIDL.Student;
import com.dandy.AIDL.IMyServiceResult;
interface IMyService{
List<Student> getStudent();
int addStudent(in Student student);
void result(in IMyServiceResult resultService);
}
IMyService是主体,既是让我们外部实现调用的,而IMyServiceResult是作为IMyService中的一个方法中的参数。
4.实现接口
创建一个服务类来实现刚才定义的IMyService.aidl接口,代码如下:
package com.dandy.service;
import java.util.ArrayList;
import java.util.List;
import com.dandy.AIDL.IMyService;
import com.dandy.AIDL.IMyServiceResult;
import com.dandy.AIDL.Student;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service{
private List<Student> mStudents = new ArrayList<Student>();
private static final String PACKAGE_SAYHI = "com.example.aidlclient";
private boolean mCanRun = true;
private int counter = 0;
private IMyServiceResult resultService;
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public List<Student> getStudent() throws RemoteException {
synchronized (mStudents) {
return mStudents;
}
}
@Override
public int addStudent(Student student) throws RemoteException {
if(!mStudents.contains(student)){
mStudents.add(student);
return 1;
}
return 0;
}
@Override
public void result(IMyServiceResult resultService)throws RemoteException {
MyService.this.resultService = resultService;
};
/**
*权限验证
*/
@Override
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];
}
if(!PACKAGE_SAYHI.equals(packageName)){
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public void onCreate() {
Thread thread = new Thread(null,new ServiceWorkerTest(),"BackgroundServiceWorkerTest");
thread.start();
for (int i = 1; i < 6; i++) {
Student student = new Student();
student.name = "student:" + i;
student.age = i * 5;
mStudents.add(student);
}
};
@Override
public void onDestroy(){
mCanRun = false;
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class ServiceWorkerTest implements Runnable {
@Override
public void run() {
while (mCanRun) {
if(resultService != null){
try {
resultService.backResult(counter);
} catch (RemoteException e) {
e.printStackTrace();
}
}
counter++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
在这里会看到一个IMyService.Stub类,这个类继承自IBinder类,也就是说这个MyService和普通的service
类没有区别,只不过这个这个类是返回的实现AIDL的IBinder对象。
(5)客户端获取,代码如下:
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyService = IMyService.Stub.asInterface(service);
}
};
MyService的绑定以及解绑与普通的Service没有区别。
(6)方法的调用
findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Student student = mIMyService.getStudent().get(0);
Log.i("TAG", "----->student:"+student.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Student student = new Student();
student.age = 100;
student.name = "dandy";
student.sex = Student.SEX_MALE;
try {
int result = mIMyService.addStudent(student);
Log.i("TAG", "------>result:"+result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.result).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
mIMyService.result(new IMyServiceResult.Stub() {
@Override
public void backResult(int result) throws RemoteException {
Log.i("TAG", "--------------->result:"+result);
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
(7)AndroidManifest.xml中service配置
<service android:name="com.dandy.service.MyService"
android:process=":remote"
android:exported="true">
<intent-filter >
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.dandy.service.MyService"/>
</intent-filter>
</service>
android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",
没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
注:以上内容结合了部分网络上资源内容,还望原著见谅!