本文主要是介绍Android IPC机制(一):序列化与反序列化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Android IPC机制(一):序列化与反序列化
http://blog.csdn.net/a553181867/article/details/51103810
http://blog.csdn.net/a553181867/article/details/51103810
http://blog.csdn.net/a553181867/article/details/51103810
http://blog.csdn.net/a553181867/article/details/51103810
一、前言
对于Android开发者来说,IPC机制肯定不陌生,而作为Android的进阶也必须掌握IPC机制。所谓IPC机制,即进程间通讯(Inter-Process Communication) 。我们的应用有时候出于业务需要,可能是多进程的,而由于不同进程是不共享一个内存池的,所以进程之间不能直接通讯,而要通过一些特别的机制才能通讯,所以IPC机制是解决进程间通讯的一个方案。为了熟练掌握Android的IPC机制,我们先从基本的序列化与反序列化说起。
二、序列化与反序列化
由于在系统底层,数据的传输形式是简单的字节序列形式传递,即在底层,系统不认识对象,只认识字节序列,而为了达到进程通讯的目的,需要先将数据序列化,而序列化就是将对象转化字节序列的过程 。相反地,当字节序列被运到相应的进程的时候,进程为了识别这些数据,就要将其反序列化,即把字节序列转化为对象 。有了以上理解,接下来我们认识两个用于序列化和反序列化的接口:Serializable接口和Parcelable接口。
三、Serializable接口
Java提供了一个序列化接口,serialable接口,该接口在文档中定义如下: Marks classes that can be serialized by ObjectOutputStream and deserialized by ObjectInputStream.从这句话可看出,该接口只是标记了当前类是可以序列化的,是一个空接口,仅仅提供了标志功能,具体的序列化与反序列化操作是由ObjectOutputStream和ObjectInputStream完成的。
继续读文档,发现该接口要求我们在实现了该接口的类中声明如下的一个变量:
private static final long serialVersionUID= 1L;
这个变量有什么用呢?试想一下,如果没有手动指定该值,一开始序列化了classA,得到文件A,接着对classA的内部结构更改,比如添加了一个新的变量,那么此时反序列化则会失败,因为实际上系统在序列化的时候,会自动计算出一个serialVersionUID值,并保存在已经序列化好的数据中,此时修改了classA,那么反序列化的时候系统就会重新计算一个新的serialVersionUID值,那么两个值就会不相等,就会反序列化失败。所以,手动指定一个值,能很大程度上保存数据,防止数据丢失。
接下来,我们来看一下序列化和反序列化的具体步骤 :
对象的序列化:
(1)实例化一个对象输出流:ObjectOutputStream,该对象输出流可以包装一个输出流,比如文件输出流。
(2)使用ObjectOutputStream.writeObject(obj)进行写对象。
对象的反序列化:
(1)实例化一个对象输入流:ObjectInputStream,该对象输入流可以包装一个输入流,比如文件输入流。
(2)使用ObjectInputStream.readObject(obj)进行读对象。
以下是一个实现序列化与反序列化的范例:
①User类,被序列化的类:
package com.chenyu.serialable; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; public int id; public String username; public String email; public User( int id, String username, String email) { this .id = id; this .username = username; this .email = email; } }
②Test测试类,测试序列化与反序列化是否成功:
package com.chenyu.serialable; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { User user =new User( 1 , "TestName" , "example@126.com" ); ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream( "test.txt" )); objectOutputStream.writeObject(user); objectOutputStream.close(); System.out.println("序列化成功!" ); ObjectInputStream objectInputStream =new ObjectInputStream( new FileInputStream( "test.txt" )); User newUser = (User) objectInputStream.readObject(); objectInputStream.close(); System.out.println("反序列化成功!" ); System.out.println("ID:" +newUser.id+ " username:" +newUser.username+ " Email:" +newUser.email); } }
运行Test.java,得到如下结果:
可知道,该类已经序列化和反序列化成功了。
注意: 静态成员变量属于类,而不是对象,所以不会参与序列化;使用transient关键字标记的成员变量不参与序列化过程。
四、Parcelable接口
接下来我们要说的是Parcelable接口,该接口是Android提供的新的序列化方式。首先,先看官方文档对该接口的描述: Interface for classes whose instances can be written to and restored from a Parcel
. Classes implementing the Parcelable interface must also have a static field called CREATOR
, which is an object implementing the Parcelable.Creator
interface.
除了实现该接口的方法外,还需创建一个名叫CREATOR的静态对象,该对象实现了一个Parcelable.Creator的匿名内部类。以下是官方文档的一个类实现Parcelable接口的典型例子:
public class MyParcelable implements Parcelable { private int mData; public int describeContents() { return 0 ; } public void writeToParcel(Parcel out, int flags) { out.writeInt(mData); } public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() { public MyParcelable createFromParcel(Parcel in) { return new MyParcelable(in); } public MyParcelable[] newArray( int size) { return new MyParcelable[size]; } }; private MyParcelable(Parcel in) { mData = in.readInt(); } }
下面介绍一下以上各个方法的作用:
①writeToParcel(Parcel out,int flags):将当前对象写入序列化结构之中。
②createFromParcel(Parcel in):从序列化后的对象中创建原始对象
③newArray(int size):创建指定长度的原始对象数组
④MyParcelable(Parcel in):从序列化后的对象中创建原始对象
由以上各个方法可知,writeToParcel方法负责将对象序列化,而CREATOR负责数据的反序列化,只要你的类实现了Parcelable接口,并实现以上方法,那么就能自动地对对象进行序列化和反序列化了。
注意: 在writeToParcel方法中,调用了out.writeInt(data)方法,如果当前类有多个属性,比如:int id,String name,String email,那么方法体可以写为:
out.writeInt(id); out.writeString(name); out.writeString(email);
这样写后,在相应的MyParcelable(Parcel in)反序列化方法也必须如下写:
in.readInt(); in.readString(); in.readString();
即顺序应该一一对应, 否则,取出来的数据将会出错。
到目前为止,介绍了Serialable接口和Parcelable接口,这是IPC机制中比较基础的概念,应熟练掌握。希望我的文章能对你们的学习起到帮助作用。
Android IPC机制(二):AIDL的基本使用方法
标签: ipcandroidAIDL
2016-04-10 13:22 370人阅读 收藏 举报
分类: Android(33)
版权声明:本文为博主原创文章,未经博主允许不得转载。
一、前言
上一篇博客,讲述了实现序列化和反序列化的基本方式,是实现进程间通讯的必要条件,而这篇博客主要来讲一讲AIDL,通过展示AIDL的基本使用方法来引出IPC的核心:Binder。
二、什么是AIDL?
AIDL全称:Android Interface Definition Language,即Android接口定义语言。由于不同的进程不能共享内存,所以为了解决进程间通讯的问题,Android使用一种接口定义语言来公开服务的接口 ,本质上,AIDL非常像一个接口,通过公开接口,让别的进程调用该接口,从而实现进程间的通讯。
三、使用AIDL
以下结合一个具体实例来示范AIDL的使用方法。
1、建立.aidl文件
为了方便AIDL的开发,建议把所有和AIDL相关的类和文件放入同一个包中,这样方便把整个包复制,以便其他模块或者应用需要用到同一个AIDL。在Android Studio下,专门为AIDL文件创建了一个文件夹,方便我们的管理:
可以看到,笔者新建了一个service模块,该模块在manifests的声明如下:
< service android:name = ".MyAidlService" android:enabled = "true" android:exported = "true" > </ service > 这个模块为app模块提供服务,与app模块处于不同进程,所以模拟了进程间通讯的场景。在service模块,新建一个AIDL文件夹,然后新建一个包,这里包名为com.chenyu.service,然后新建AIDL文件:IMyAidl.aidl:
package com.chenyu.service; import com.chenyu.service.Person; interface IMyAidl { void addPerson(in Person person); List<Person> getPersonList(); } 这与定义一个接口的语法基本相同,都是以Interface为关键字定义。里面声明了两个方法,分别是addPerson(in Person person)与getPersonList()。AIDL中除了基本数据类型,其他类型的参数必须标上方向,in、out、或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数,我们要根据需要实际指定参数类型,因为底层的数据处理开销非常大,如果不指定类型,编译将会无法通过。
AIDL支持的数据类型 :①基本数据类型(int,long,char,boolean,double)②string和CharSequence,③List:只支持ArrayList,以及里面所有的元素必须能够被AIDL支持 ④Map:只支持HashMap,以及里面所有的元素必须能够被AIDL支持 ⑤Parcelable:所有实现了Parcelable接口的对象 ⑥AIDL:所有AIDL接口本身也可以在AIDL文件中使用。
注意一下: 这里使用了自定义的Parcelable对象:Person类,但是AIDL不认识这个类,所以我们要创建一个与Person类同名的AIDL文件:Person.aidl
package com.chenyu.service; parcelable Person; 只有这样,IMyAidl.aidl才能知道其中的Person是使用了Parcelable接口的类,注意,Person类的包名与Person.aidl的包名一定要相同,即无论其他应用或者其他模块,只要有AIDL,都应该保证AIDL的所有包结构一致,才能保证顺利进行IPC通讯,减少不必要的麻烦。
2、Person类,实现Parcelable接口
在java文件夹中,创建com.chenyu.service包,这样就与上面的是相同包名了。
package com.chenyu.service; import android.os.Parcel; import android.os.Parcelable; public class Person implements Parcelable { private String name; private int age; private int number; public Person(Parcel source) { this .name=source.readString(); this .age=source.readInt(); this .number=source.readInt(); } public Person( int age, String name, int number) { this .age = age; this .name = name; this .number = number; } @Override public int describeContents() { return 0 ; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); dest.writeInt(number); } public static final Parcelable.Creator<Person> CREATOR= new Creator<Person>() { @Override public Person createFromParcel(Parcel source) { return new Person(source); } @Override public Person[] newArray( int size) { return new Person[size]; } }; @Override public String toString() { return "Person{" + "name='" + name + '\ '' + ", age=" + age + ", number=" + number + '}' ; } } 对于Parcelable接口的详细解析,可参考上一篇博客,这里不再赘述。
3、实现服务端
上面我们定义了一个AIDL接口,接下来要做的是实现这个AIDL接口,在java/com.chenyu.service中,创建MyAidlService.java文件:
package com.chenyu.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; import java.util.List; public class MyAidlService extends Service { private ArrayList<Person> persons; @Override public IBinder onBind(Intent intent) { persons=new ArrayList<Person>(); Log.d("cy" , "success bind" ); return iBinder; } private IBinder iBinder= new IMyAidl.Stub() { @Override public void addPerson(Person person) throws RemoteException { persons.add(person); } @Override public List<Person> getPersonList() throws RemoteException { return persons; } }; @Override public void onCreate() { super .onCreate(); Log.d("cy" , "onCreate " ); } } 我们来看一下服务端是如何实现接口的:
(1)为了实现来自.aidl文件生成的接口,需要继承Binder接口(例如Ibinder接口),并且实现从.aidl文件中继承的方法,在上面代码中,使用匿名实例实现一个叫IMyAidl(定义在IMyAidl.aidl中)的接口,实现了两个方法,addPerson和getPersonList.
(2)onBind方法:该方法在客户端与服务端连接的时候回调,实现客户端和服务端的绑定,并返回一个Binder实例,这里返回的是iBinder,而IBinder是(1)中实现了接口的匿名实例,即客户端拿到的实际上实现了接口的一个实例,这样,客户端通过Binder就与服务端建立了连接,客户端通过Binder远程调用服务端的实例方法,这样也即实现了进程间通讯。
4、实现客户端
在实现客户端之前,先确保把aidl的包复制过来,就相上面笔者所给出的结构图一样,包括Person类也应该复制过来。显示界面比较简单,就不贴出来了,主要看Activity的代码:
package com.chenyu.myaidl; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.Button; import com.chenyu.service.IMyAidl; import com.chenyu.service.Person; import java.util.List; public class MainActivity extends Activity implements View.OnClickListener { private Button btn; IMyAidl iMyAidl; private ServiceConnection conn= new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("cylog" , "onServiceConnected success" ); iMyAidl=IMyAidl.Stub.asInterface(service);<span style="white-space:pre" > </span> } @Override public void onServiceDisconnected(ComponentName name) { Log.d("cylog" , "onServicedisConnected " ); iMyAidl=null ; } }; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); bindService(); } private void initView() { btn= (Button) findViewById(R.id.cal); btn.setOnClickListener(this ); } @Override public void onClick(View v) { try { iMyAidl.addPerson(new Person( 21 , "陈育" , 22255 )); List<Person> persons = iMyAidl.getPersonList(); Log.d("cylog" ,persons.toString()); } catch (RemoteException e) { e.printStackTrace(); } } private void bindService() { Intent intent=new Intent(); intent.setComponent(new ComponentName( "com.chenyu.service" , "com.chenyu.service.MyAidlService" )); bindService(intent,conn, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super .onDestroy(); unbindService(conn); } } 与绑定一般Service的语法差不多,在安卓5.0之后,必须显式指定Service的包名和类名即:
private void bindService() { Intent intent=new Intent(); intent.setComponent(new ComponentName( "com.chenyu.service" , "com.chenyu.service.MyAidlService" )); bindService(intent,conn, Context.BIND_AUTO_CREATE); } 在bindService(intent,conn,Context.BIND_AUTO_CREATE)方法中有几个参数需要说明一下,
①conn:该参数代表了与服务端的连接,即ServiceConnection.
②Context.BIND_AUTO_CREATE:该参数表示绑定的同时创建一个Service。 在发出绑定成功之后,会回调①处的代码,此时,可在回调方法onServiceConnected()方法中,获取服务器返回的IMyAidl实例,在客户端拿到该实例之后,就可以通过调用相应的方法进行远程通讯了,比如上述的②处代码。
最后,看一下运行结果,先运行service,然后运行app:
可以看出,的确是构成了进程间通讯,并且完成了进程间通讯。
以上是利用AIDL实现进程通讯的基本方法,希望对大家有所帮助。关于AIDL的核心原理以及Binder,AIDL注意事项,会在下一篇博客更新。
Android IPC机制(三):浅谈Binder的使用
标签: AIDLandroidipc原理优化
2016-04-11 20:19 711人阅读 收藏 举报
分类: Android(33)
版权声明:本文为博主原创文章,未经博主允许不得转载。
一、前言
在上一篇博客Android IPC机制(二):AIDL的基本使用方法中,笔者讲述了安卓进程间通讯的一个主要方式,利用AIDL进行通讯,并介绍了AIDL的基本使用方法。其实AIDL方式利用了Binder来进行跨进程通讯,Binder是Android中的一种跨进程通讯方式,其底层实现原理比较复杂,限于笔者水平,不能展开详谈,所以这篇文章主要谈谈以AIDL为例,谈谈Binder的使用方法。
二、原理
上一篇文章中创建了一个IMyAidl.aidl文件,即接口文件,随即编译了该文件,生成了一个.java文件,该文件在gen目录下:
打开该文件,得到如下代码:
<span style= "font-size:18px;" > package com.chenyu.service; public interface IMyAidl extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.chenyu.service.IMyAidl { ...... public void addPerson(com.chenyu.service.Person person) throws android.os.RemoteException; public java.util.List<com.chenyu.service.Person> getPersonList() throws android.os.RemoteException;</span> } 其中省略了一部分,我们先从大体上认识,然后在深入。
(1)从大体上看,该java文件是一个接口,继承了IInterface接口,接着,声明了一个静态内部抽象类:Stub,然后是两个方法,可以看到,这两个方法分别是原IMyAidl.aidl文件内声明的两个方法。
(2)我们看回Stub类,它继承了Binder,同时实现了IMyAidl 。这个类实现了自己的接口!那么可想而知,该接口所声明的addPerson,getPersonList方法,将会在Stub类得到实现,具体如何实现,我们展开Stub类:
public static abstract class Stub extends android.os.Binder implements com.chenyu.service.IMyAidl { private static final java.lang.String DESCRIPTOR = "com.chenyu.service.IMyAidl" ; public Stub() {<span style= "white-space:pre" > </span> this .attachInterface( this , DESCRIPTOR); } public static com.chenyu.service.IMyAidl asInterface(android.os.IBinder obj) { if ((obj == null )) { return null ; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null ) && (iin instanceof com.chenyu.service.IMyAidl))) { return ((com.chenyu.service.IMyAidl) iin); } return new com.chenyu.service.IMyAidl.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() {<span style= "white-space:pre" > </span> return this ; } @Override public boolean onTransact( int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {<span style= "white-space:pre" > </span> switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true ; } case TRANSACTION_addPerson: { data.enforceInterface(DESCRIPTOR); com.chenyu.service.Person _arg0; if (( 0 != data.readInt())) { _arg0 = com.chenyu.service.Person.CREATOR.createFromParcel(data); } else { _arg0 = null ; } this .addPerson(_arg0); reply.writeNoException(); return true ; } case TRANSACTION_getPersonList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.chenyu.service.Person> _result = this .getPersonList(); reply.writeNoException(); reply.writeTypedList(_result); return true ; } } return super .onTransact(code, data, reply, flags); } private static class Proxy implements com.chenyu.service.IMyAidl {<span style= "white-space:pre" > </span> ... } static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0 ); static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1 ); } (3)从上往下地,我们逐个分析一下各个方法或者变量的作用:
①Stub()构造方法:此方法调用了父类Binder的attachInterface()方法,将当前的Interface与Binder联系起来,由于传递了DESCRIPTOR这个参数,唯一标识了当前Interface。
②asInterface(IBinder obj) :静态方法,传递了一个接口对象 ,该对象从哪里传递进来的呢?我们来看看上一章博客的客户端代码:
public void onServiceConnected(ComponentName name, IBinder service) { Log.d("cylog" , "onServiceConnected success" ); iMyAidl=IMyAidl.Stub.asInterface(service); } 在这里,可以看到,调用了IMyAidl.Stub.asInterface(service)方法,即上面的②号方法,并且把service传递了进去,我们接着往下看:
public static com.chenyu.service.IMyAidl asInterface(android.os.IBinder obj) { if ((obj == null )) { return null ; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null ) && (iin instanceof com.chenyu.service.IMyAidl))) { return ((com.chenyu.service.IMyAidl) iin); } return new com.chenyu.service.IMyAidl.Stub.Proxy(obj); } 首先判断obj是否有效,如果无效直接返回Null,说明客户端与服务端的连接失败了。接着调用了obj.queryLocalInterface(DESCRIPTOR)方法,为IInterface的对象赋值,注意到 这里再次传递了DESCRIPTOR参数,可以猜测,这个方法应该是查找与当前Interface相关的一个方法,我们看看IBinder接口的queryLocalInterface()方法:
public IInterface queryLocalInterface(String descriptor); 大概意思是说,根据descriptor的值,试图为Binder取回一个本地的interface,其中local意思应为当前进程 ,如果返回值是null,那么应该实例化一个proxy类。在了解了obj.queryLocalInterface(DESCRIPTOR)方法后,我们再次回到 asInterface(obj)方法 ,继续往下看:接着是一个if判断,主要判断客户端与服务端是否处于同一进程,如果处于同一进程,那么直接返回了Stub对象本身,如果不是同一个进程,那么就会新建一个Proxy代理类 (下面会提到)。
③asBinder():此方法用于返回当前对象本身。 ④onTransact(int code,Parcel data,Parcel reply,int flags):该方法一般运行在服务端中的Binder线程池中,即远程请求会在该方法得到处理。传递的code值用于判断客户端的请求目标,是addPerson或者是getPersonList。我们以请求目标为addPerson()为例分析一下,提取其主要函数体如下:
case TRANSACTION_addPerson: { data.enforceInterface(DESCRIPTOR); com.chenyu.service.Person _arg0; if (( 0 != data.readInt())) { _arg0 = com.chenyu.service.Person.CREATOR.createFromParcel(data); } else { _arg0 = null ; } this .addPerson(_arg0); reply.writeNoException(); return true ; } 首先声明了_arg0是Person类的对象,接着,以data为参数调用了Person类的CREATOR.createFromParcel 方法,反序列化 生成了Person类,这也是为什么实现了Parcelable接口的类应该同时实现CREATOR ,原来在这里调用了反序列化的方法。接着,调用this.addPerson(_arg0)方法,注意: 这里的this代表当前的Binder对象,那么由Binder调用的addPerson(_arg0)方法,实际上是由绑定到Binder的service调用的,即服务端调用了自身的addPerson方法。为了方便明白,让我们来回顾一下上一篇文章服务端的代码:
private IBinder iBinder= new IMyAidl.Stub() { @Override public void addPerson(Person person) throws RemoteException { persons.add(person); } @Override public List<Person> getPersonList() throws RemoteException { return persons; } }; 是不是一下子就明白了?IMyAidl.Stub()实现的接口,其中的方法在服务端得到了实现:addPerson和getPersonList()。当在Binder线程池中,调用了this.addPerson()方法,实际上回调了服务端的addPerson方法,而底层到底是怎么实现的,限于笔者的水平,暂时不了解,等以后笔者再深入了解Binder的工作机制再回答这个问题。
好了,回到当前的类,我们继续往下看: ⑤private static class Proxy:这里又出现了一个私有的静态内部类,关于这个类将在接下来详细讲述。
⑥最后两行代码分别是两个常量,标志了两个方法,即上面提到的code值。
(4)Proxy类,也实现了IMyAidl接口,同时实现了addPerson和getPersonList的方法。而Proxy类在哪里被实例化的呢? 是上面(3)②中,当客户端与服务端不在同一个进程的时候,就会实例化这个代理类,并返回给客户端。什么叫做代理类呢? 所谓代理,即一个中介,客户端拿到的实例,能操作服务端的部分功能,让客户端以为自己已经拿到了服务端的实例,其实不是,只是拿到服务端的一个代理而已。接下来我们展开该类,看看内部:
private static class Proxy implements com.chenyu.service.IMyAidl { 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 void addPerson(com.chenyu.service.Person person) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((person != null )) { _data.writeInt(1 ); person.writeToParcel(_data, 0 ); } else { _data.writeInt(0 ); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0 ); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.util.List<com.chenyu.service.Person> getPersonList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.chenyu.service.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0 );<span style= "white-space:pre" > </span> _reply.readException(); _result = _reply.createTypedArrayList(com.chenyu.service.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } } 这里关注两个接口方法的实现:addPerson和getPersonList,这两个方法已经多次出现了,而在代理类实现的这两个方法,是运行在客户端的!!! 其主要实现过程是这样的:当客户端拿到代理类,调用addPerson或者getPersonList方法,首先会创建输入型Parcel对象_data和输出型Parcel对象_reply,接着调用①号代码调用transact来发起RPC远程请求,同时当前线程会被挂起 ,此时,服务端的onTransact会被调用,即上面所说的(3)④号代码,当服务端处理完请求后,会返回数据,当前线程继续执行知道返回 _result结果。
至此,对于IPC的方式之一——AIDL的原理已经剖析完毕,接下来总结 一下:
1、客户端发出绑定请求,服务端和客户端绑定在同一个Binder上。客户端执行asInterface()方法,如果客户端和服务端处于同一进程,则直接返回服务端的Stub对象本身,如果处于不同进程,则返回的是Stub.proxy代理类对象。
2、客户端发送远程请求(addPerson或者getPersonList),此时客户端线程挂起,Binder拿到数据后,对数据进行处理如在不同进程,会把数据写入Parcel,调用Transact方法。
3、触发onTransact方法,该方法运行在Binder线程池,方法中会调用到服务端实现的接口方法,当数据处理完毕后,返回reply值,经过Binder返回客户端,此时客户端线程被唤醒。
三、优化
最后说一说如何优化AIDL,上面提到,客户端发送请求后,会被挂起,这意味着,如果处理数据的时间过长,那么该线程就一直等不到唤醒,这是很严重的,如果在UI线程发送请求,会直接导致ANR,所以我们需要在子线程发送异步请求 ,这样才能避免ANR。还有一点,Binder可能是意外死亡的,如果Binder意外死亡,那么子线程可能会一直挂起,所以我们要启用重新连接服务。有两个方法,一个是给Binder设置DeathRecipient监听 ,另一个是在onServiceDisconnected中重连服务 。
参考书籍:《Android 开发艺术探索》 任玉刚著,2015年9月第一版
http://blog.csdn.net/a553181867/article/details/51125124
http://blog.csdn.net/a553181867/article/details/51125124
http://blog.csdn.net/a553181867/article/details/51125124
http://blog.csdn.net/a553181867/article/details/51125124
这篇关于Android IPC机制(一):序列化与反序列化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!