aidl parcelableWord文件下载.docx
《aidl parcelableWord文件下载.docx》由会员分享,可在线阅读,更多相关《aidl parcelableWord文件下载.docx(11页珍藏版)》请在冰点文库上搜索。
而调用(call)来自local进程或者remote进程,有什么区别呢?
尤其是以下情况(引用原文,不作翻译了,以免翻译有误):
∙Callsmadefromthelocalprocessareexecutedinthesamethreadthatismakingthecall.IfthisisyourmainUIthread,thatthreadcontinuestoexecuteintheAIDLinterface.Ifitisanotherthread,thatistheonethatexecutesyourcodeintheservice.Thus,ifonlylocalthreadsareaccessingtheservice,youcancompletelycontrolwhichthreadsareexecutinginit(butifthatisthecase,thenyoushouldn'
tbeusingAIDLatall,butshouldinsteadcreatetheinterfacebyimplementingaBinder).
∙Callsfromaremoteprocessaredispatchedfromathreadpooltheplatformmaintainsinsideofyourownprocess.Youmustbepreparedforincomingcallsfromunknownthreads,withmultiplecallshappeningatthesametime.Inotherwords,animplementationofanAIDLinterfacemustbecompletelythread-safe.
∙Theonewaykeywordmodifiesthebehaviorofremotecalls.Whenused,aremotecalldoesnotblock;
itsimplysendsthetransactiondataandimmediatelyreturns.TheimplementationoftheinterfaceeventuallyreceivesthisasaregularcallfromtheBinderthreadpoolasanormalremotecall.Ifonewayisusedwithalocalcall,thereisnoimpactandthecallisstillsynchronous.
定义AIDL接口
AIDL接口文件,和普通的接口内容没有什么特别,只是它的扩展名为.aidl。
保存在src目录下。
如果其他应用程序需要IPC,则那些应用程序的src也要带有这个文件。
AndroidSDKtools就会在gen目录自动生成一个IBinder接口文件。
service必须适当地实现这个IBinder接口。
那么客户端程序就能绑定这个service并在IPC时从IBinder调用方法。
每个aidl文件只能定义一个接口,而且只能是接口的声明和方法的声明。
1.创建.aidl文件
AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。
这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。
其中对于Java编程语言的基本数据类型(int,long,char,boolean等),String和CharSequence,集合接口类型List和Map,不需要import语句。
而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。
AIDL允许传递实现Parcelable接口的类,需要import.
需要特别注意的是,对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
AIDL只支持接口方法,不能公开static变量。
例如(IMyService.aidl):
packagecom.demo;
importcom.demo.Person;
interfaceIMyService{
voidsavePersonInfo(inPersonperson);
List<
Person>
getAllPerson();
}
2.实现接口
创建一个类实现刚才那个aidl的接口:
publicclassRemoteServiceextendsService{
privateLinkedList<
personList=newLinkedList<
();
@Override
publicIBinderonBind(Intentintent){
returnmBinder;
}
privatefinalIMyService.StubmBinder=newIMyService.Stub(){
publicvoidsavePersonInfo(Personperson)throwsRemoteException{
if(person!
=null){
personList.add(person);
publicList<
getAllPerson()throwsRemoteException{
returnpersonList;
};
这里会看到有一个名为IMyService.Stub类,查看aidl文件生成的Java文件源代码就能发现有这么一段代码:
/**Local-sideIPCimplementationstubclass.*/
publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.demo.IMyService
原来Stub类就是继承于Binder类,也就是说RemoteService类和普通的Service类没什么不同,只是所返回的IBinder对象比较特别,是一个实现了AIDL接口的Binder。
接下来就是关于所传递的数据Bean——Person类,是一个序列化的类,这里使用Parcelable接口来序列化,是Android提供的一个比Serializable效率更高的序列化类。
Parcelable需要实现三个函数:
1)voidwriteToParcel(Parceldest,intflags)将需要序列化存储的数据写入外部提供的Parcel对象dest。
而看了网上的代码例子,个人猜测,读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。
具体情况我没试验过!
2)describeContents()没搞懂有什么用,反正直接返回0也可以
3)staticfinalParcelable.Creator对象CREATOR
这个CREATOR命名是固定的,而它对应的接口有两个方法:
createFromParcel(Parcelsource)实现从source创建出JavaBean实例的功能
newArray(intsize)创建一个类型为T,长度为size的数组,仅一句话(returnnewT[size])即可。
估计本方法是供外部类反序列化本类数组使用。
仔细观察Person类的代码和上面所说的内容:
publicclassPersonimplementsParcelable{
privateStringname;
privateStringtelNumber;
privateintage;
publicPerson(){}
publicPerson(Parcelpl){
name=pl.readString();
telNumber=pl.readString();
age=pl.readInt();
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
this.name=name;
publicStringgetTelNumber(){
returntelNumber;
publicvoidsetTelNumber(StringtelNumber){
this.telNumber=telNumber;
publicintgetAge(){
returnage;
publicvoidsetAge(intage){
this.age=age;
publicintdescribeContents(){
return0;
publicvoidwriteToParcel(Parceldest,intflags){
dest.writeString(name);
dest.writeString(telNumber);
dest.writeInt(age);
publicstaticfinalParcelable.Creator<
CREATOR=newParcelable.Creator<
(){
publicPersoncreateFromParcel(Parcelsource){
returnnewPerson(source);
publicPerson[]newArray(intsize){
returnnewPerson[size];
然后创建Person.aidl文件,注意这里的parcelable和原来实现的Parcelable接口,开头的字母p一个小写一个大写:
parcelablePerson;
对于实现AIDL接口,官方还提醒我们:
1.调用者是不能保证在主线程执行的,所以从一调用的开始就需要考虑多线程处理,以及确保线程安全;
2.IPC调用是同步的。
如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。
也就是IPC调用会挂起应用程序导致界面失去响应,这种情况应该考虑单独开启一个线程来处理。
3.抛出的异常是不能返回给调用者(跨进程抛异常处理是不可取的)。
3.客户端获取接口
客户端如何获取AIDL接口呢?
通过IMyService.Stub.asInterface(service)来得到IMyService对象:
privateIMyServicemRemoteService;
privateServiceConnectionmRemoteConnection=newServiceConnection(){
publicvoidonServiceConnected(ComponentNameclassName,IBinderservice){
mRemoteService=IMyService.Stub.asInterface(service);
}
publicvoidonServiceDisconnected(ComponentNameclassName){
mRemoteService=null;
在生成的IMyService.java里面会找到这样的代码:
/**
*CastanIBinderobjectintoancom.demo.IMyServiceinterface,
*generatingaproxyifneeded.
*/
publicstaticcom.demo.IMyServiceasInterface(android.os.IBinderobj){...}
而service的绑定没有什么不同:
if(mIsRemoteBound){
unbindService(mRemoteConnection);
}else{
bindService(newIntent("
com.demo.IMyService"
),
mRemoteConnection,Context.BIND_AUTO_CREATE);
mIsRemoteBound=!
mIsRemoteBound;
通过IPC调用/传递数据
客户端绑定service后就能通过IPC来调用/传递数据了,直接调用service对象的接口方法:
addPersonButton.setOnClickListener(
newView.OnClickListener(){
privateintindex=0;
publicvoidonClick(Viewview){
Personperson=newPerson();
index=index+1;
person.setName("
Person"
+index);
person.setAge(20);
person.setTelNumber("
123456"
);
try{
mRemoteService.savePersonInfo(person);
}catch(RemoteExceptione){
e.printStackTrace();
});
listPersonButton.setOnClickListener(
list=null;
list=mRemoteService.getAllPerson();
if(list!
StringBuildertext=newStringBuilder();
for(Personperson:
list){
text.append("
\nPersonname:
"
text.append(person.getName());
\n
age:
text.append(person.getAge());
\ntelnumber:
text.append(person.getTelNumber());