在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类
Student是一个实现了Parcelable接口序列化的类,实现此接口必须要实现以下三个接口:
(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(); } } } } }
(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(); } } });
<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",
没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
注:以上内容结合了部分网络上资源内容,还望原著见谅!
资源文件下载。点击此处!