Android IPC机制(一):序列化与反序列化

2024-06-19 13:08
文章标签 序列化 android ipc 机制

本文主要是介绍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完成的。

继续读文档,发现该接口要求我们在实现了该接口的类中声明如下的一个变量:

[java]  view plain copy
print ?
  1. 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类,被序列化的类:

[java]  view plain copy
print ?
  1. package com.chenyu.serialable;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class User implements Serializable {  
  6.   
  7.     private static final long serialVersionUID = 1L;  
  8.       
  9.     public int id;  
  10.     public String username;  
  11.     public String email;  
  12.       
  13.     public User(int id, String username, String email) {  
  14.         this.id = id;  
  15.         this.username = username;  
  16.         this.email = email;  
  17.     }  
  18.       
  19.   
  20. }  
②Test测试类,测试序列化与反序列化是否成功:

[java]  view plain copy
print ?
  1. package com.chenyu.serialable;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.io.ObjectInputStream;  
  8. import java.io.ObjectOutputStream;  
  9.   
  10. public class Test {  
  11.     public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {  
  12.         //实例化User类  
  13.         User user =new User(1,"TestName","example@126.com");  
  14.         //序列化过程  
  15.         ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.txt"));  
  16.         objectOutputStream.writeObject(user);  
  17.         objectOutputStream.close();  
  18.         System.out.println("序列化成功!");  
  19.         //反序列化过程  
  20.         ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("test.txt"));  
  21.         User newUser = (User) objectInputStream.readObject();  
  22.         objectInputStream.close();  
  23.         System.out.println("反序列化成功!");  
  24.         System.out.println("ID:"+newUser.id+"  username:"+newUser.username+"  Email:"+newUser.email);  
  25.     }  
  26. }  
运行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.Creatorinterface.

   除了实现该接口的方法外,还需创建一个名叫CREATOR的静态对象,该对象实现了一个Parcelable.Creator的匿名内部类。以下是官方文档的一个类实现Parcelable接口的典型例子:

[java]  view plain copy
print ?
  1. public class MyParcelable implements Parcelable {  
  2.      private int mData;  
  3.   
  4.      public int describeContents() {  
  5.          return 0;  
  6.      }  
  7.   
  8.      public void writeToParcel(Parcel out, int flags) {  
  9.          out.writeInt(mData);  
  10.      }  
  11.   
  12.      public static final Parcelable.Creator<MyParcelable> CREATOR  
  13.              = new Parcelable.Creator<MyParcelable>() {  
  14.          public MyParcelable createFromParcel(Parcel in) {  
  15.              return new MyParcelable(in);  
  16.          }  
  17.   
  18.          public MyParcelable[] newArray(int size) {  
  19.              return new MyParcelable[size];  
  20.          }  
  21.      };  
  22.        
  23.      private MyParcelable(Parcel in) {  
  24.          mData = in.readInt();  
  25.      }  
  26.  }  
   下面介绍一下以上各个方法的作用:

   ①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,那么方法体可以写为:

[java]  view plain copy
print ?
  1. out.writeInt(id);  
  2. out.writeString(name);  
  3. out.writeString(email);  
    这样写后,在相应的MyParcelable(Parcel in)反序列化方法也必须如下写:

[java]  view plain copy
print ?
  1. in.readInt();  
  2. in.readString();  
  3. in.readString();  
    即顺序应该一一对应,否则,取出来的数据将会出错。


到目前为止,介绍了Serialable接口和Parcelable接口,这是IPC机制中比较基础的概念,应熟练掌握。希望我的文章能对你们的学习起到帮助作用。








Android IPC机制(二):AIDL的基本使用方法

标签: ipcandroidAIDL
  370人阅读  评论(2)  收藏  举报
  分类:

一、前言

上一篇博客,讲述了实现序列化和反序列化的基本方式,是实现进程间通讯的必要条件,而这篇博客主要来讲一讲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的声明如下:

[html]  view plain copy
print ?
  1. <service android:name=".MyAidlService"  
  2.             android:enabled="true"  
  3.             android:exported="true"></service>  
这个模块为app模块提供服务,与app模块处于不同进程,所以模拟了进程间通讯的场景。在service模块,新建一个AIDL文件夹,然后新建一个包,这里包名为com.chenyu.service,然后新建AIDL文件:IMyAidl.aidl:

[java]  view plain copy
print ?
  1. // IMyAidl.aidl  
  2. package com.chenyu.service;  
  3.   
  4. // Declare any non-default types here with import statements  
  5. import com.chenyu.service.Person;  
  6. interface IMyAidl {  
  7.     void addPerson(in Person person);  
  8.     List<Person> getPersonList();  
  9. }  
这与定义一个接口的语法基本相同,都是以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

[java]  view plain copy
print ?
  1. // IMyAidl.aidl  
  2. package com.chenyu.service;  
  3. parcelable Person;  
只有这样,IMyAidl.aidl才能知道其中的Person是使用了Parcelable接口的类,注意,Person类的包名与Person.aidl的包名一定要相同,即无论其他应用或者其他模块,只要有AIDL,都应该保证AIDL的所有包结构一致,才能保证顺利进行IPC通讯,减少不必要的麻烦。


2、Person类,实现Parcelable接口

在java文件夹中,创建com.chenyu.service包,这样就与上面的是相同包名了。

[java]  view plain copy
print ?
  1. package com.chenyu.service;  
  2.   
  3. import android.os.Parcel;  
  4. import android.os.Parcelable;  
  5.   
  6.   
  7. public class Person implements Parcelable {  
  8.     private String name;  
  9.     private int age;  
  10.     private int number;  
  11.   
  12.     public Person(Parcel source) {  
  13.         this.name=source.readString();  
  14.         this.age=source.readInt();  
  15.         this.number=source.readInt();  
  16.     }  
  17.   
  18.     //getter、setter method  
  19.     //...  
  20.     public Person(int age, String name, int number) {  
  21.         this.age = age;  
  22.         this.name = name;  
  23.         this.number = number;  
  24.     }  
  25.   
  26.     @Override  
  27.     public int describeContents() {  
  28.         return 0;  
  29.     }  
  30.   
  31.     @Override  
  32.     public void writeToParcel(Parcel dest, int flags) {  
  33.         dest.writeString(name);  
  34.         dest.writeInt(age);  
  35.         dest.writeInt(number);  
  36.     }  
  37.     public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {  
  38.         @Override  
  39.         public Person createFromParcel(Parcel source) {  
  40.             return new Person(source);  
  41.         }  
  42.   
  43.         @Override  
  44.         public Person[] newArray(int size) {  
  45.             return new Person[size];  
  46.         }  
  47.     };  
  48.   
  49.     @Override  
  50.     public String toString() {  
  51.         return "Person{" +  
  52.                 "name='" + name + '\'' +  
  53.                 ", age=" + age +  
  54.                 ", number=" + number +  
  55.                 '}';  
  56.     }  
  57. }  
对于Parcelable接口的详细解析,可参考上一篇博客,这里不再赘述。

3、实现服务端

上面我们定义了一个AIDL接口,接下来要做的是实现这个AIDL接口,在java/com.chenyu.service中,创建MyAidlService.java文件:

[java]  view plain copy
print ?
  1. package com.chenyu.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.os.RemoteException;  
  7. import android.util.Log;  
  8.   
  9. import java.util.ArrayList;  
  10. import java.util.List;  
  11.   
  12. public class MyAidlService extends Service {  
  13.     private ArrayList<Person> persons;  
  14.     @Override  
  15.     public IBinder onBind(Intent intent) {  
  16.         persons=new ArrayList<Person>();  
  17.         Log.d("cy""success bind");  
  18.         return iBinder;  
  19.     }  
  20.     private IBinder iBinder= new IMyAidl.Stub() {  
  21.         @Override  
  22.         public void addPerson(Person person) throws RemoteException {  
  23.             persons.add(person);  
  24.         }  
  25.   
  26.         @Override  
  27.         public List<Person> getPersonList() throws RemoteException {  
  28.             return persons;  
  29.         }  
  30.     };  
  31.   
  32.     @Override  
  33.     public void onCreate() {  
  34.         super.onCreate();  
  35.         Log.d("cy""onCreate ");  
  36.     }  
  37. }  
我们来看一下服务端是如何实现接口的:

(1)为了实现来自.aidl文件生成的接口,需要继承Binder接口(例如Ibinder接口),并且实现从.aidl文件中继承的方法,在上面代码中,使用匿名实例实现一个叫IMyAidl(定义在IMyAidl.aidl中)的接口,实现了两个方法,addPerson和getPersonList.

(2)onBind方法:该方法在客户端与服务端连接的时候回调,实现客户端和服务端的绑定,并返回一个Binder实例,这里返回的是iBinder,而IBinder是(1)中实现了接口的匿名实例,即客户端拿到的实际上实现了接口的一个实例,这样,客户端通过Binder就与服务端建立了连接,客户端通过Binder远程调用服务端的实例方法,这样也即实现了进程间通讯。

4、实现客户端

在实现客户端之前,先确保把aidl的包复制过来,就相上面笔者所给出的结构图一样,包括Person类也应该复制过来。显示界面比较简单,就不贴出来了,主要看Activity的代码:

[java]  view plain copy
print ?
  1. package com.chenyu.myaidl;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.IBinder;  
  10. import android.os.RemoteException;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.widget.Button;  
  14. import com.chenyu.service.IMyAidl;  
  15. import com.chenyu.service.Person;  
  16.   
  17. import java.util.List;  
  18.   
  19. public class MainActivity extends Activity implements View.OnClickListener {  
  20.   
  21.     private Button btn;  
  22.     IMyAidl iMyAidl;  
  23.     private ServiceConnection conn=new ServiceConnection() {  
  24.         @Override  
  25.         public void onServiceConnected(ComponentName name, IBinder service) {  
  26.             Log.d("cylog""onServiceConnected success");  
  27.             iMyAidl=IMyAidl.Stub.asInterface(service);<span style="white-space:pre">  </span>//①  
  28.   
  29.         }  
  30.   
  31.         @Override  
  32.         public void onServiceDisconnected(ComponentName name) {  
  33.             Log.d("cylog""onServicedisConnected ");  
  34.             iMyAidl=null;  
  35.         }  
  36.     };  
  37.   
  38.     @Override  
  39.     protected void onCreate(Bundle savedInstanceState) {  
  40.         super.onCreate(savedInstanceState);  
  41.         setContentView(R.layout.activity_main);  
  42.         initView();  
  43.         bindService();  
  44.     }  
  45.   
  46.     private void initView() {  
  47.         btn= (Button) findViewById(R.id.cal);  
  48.         btn.setOnClickListener(this);  
  49.     }  
  50.   
  51.     @Override  
  52.     public void onClick(View v) {  
  53.         try {  
  54.             iMyAidl.addPerson(new Person(21"陈育"22255));  
  55.             List<Person> persons = iMyAidl.getPersonList();  //②  
  56.             Log.d("cylog",persons.toString());  
  57.         } catch (RemoteException e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.     }  
  61.   
  62.     private void bindService() {  
  63.         Intent intent=new Intent();  
  64.         intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));  
  65.         bindService(intent,conn, Context.BIND_AUTO_CREATE);  
  66.     }  
  67.   
  68.     @Override  
  69.     protected void onDestroy() {  
  70.         super.onDestroy();  
  71.         unbindService(conn);  
  72.     }  
  73. }  
与绑定一般Service的语法差不多,在安卓5.0之后,必须显式指定Service的包名和类名即:

[java]  view plain copy
print ?
  1. private void bindService() {  
  2.         Intent intent=new Intent();  
  3.         intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));  
  4.         bindService(intent,conn, Context.BIND_AUTO_CREATE);  
  5.     }  
在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原理优化
  711人阅读  评论(0)  收藏  举报
  分类:

一、前言

在上一篇博客Android IPC机制(二):AIDL的基本使用方法中,笔者讲述了安卓进程间通讯的一个主要方式,利用AIDL进行通讯,并介绍了AIDL的基本使用方法。其实AIDL方式利用了Binder来进行跨进程通讯,Binder是Android中的一种跨进程通讯方式,其底层实现原理比较复杂,限于笔者水平,不能展开详谈,所以这篇文章主要谈谈以AIDL为例,谈谈Binder的使用方法。


二、原理

上一篇文章中创建了一个IMyAidl.aidl文件,即接口文件,随即编译了该文件,生成了一个.java文件,该文件在gen目录下:


打开该文件,得到如下代码:

[java]  view plain copy
print ?
  1. <span style="font-size:18px;">/* 
  2.  * This file is auto-generated.  DO NOT MODIFY. 
  3.  * Original file: G:\\Android\\Project\\MyAidl\\app\\src\\main\\aidl\\com\\chenyu\\service\\IMyAidl.aidl 
  4.  */  
  5. package com.chenyu.service;  
  6.   
  7. public interface IMyAidl extends android.os.IInterface {  
  8.     /** 
  9.      * Local-side IPC implementation stub class. 
  10.      */  
  11.     public static abstract class Stub extends android.os.Binder implements com.chenyu.service.IMyAidl {  
  12.         ......  
  13.   
  14.     public void addPerson(com.chenyu.service.Person person) throws android.os.RemoteException;  
  15.   
  16.     public java.util.List<com.chenyu.service.Person> getPersonList() throws android.os.RemoteException;</span>  
  17. }  
其中省略了一部分,我们先从大体上认识,然后在深入。

(1)从大体上看,该java文件是一个接口,继承了IInterface接口,接着,声明了一个静态内部抽象类:Stub,然后是两个方法,可以看到,这两个方法分别是原IMyAidl.aidl文件内声明的两个方法。

(2)我们看回Stub类,它继承了Binder,同时实现了IMyAidl。这个类实现了自己的接口!那么可想而知,该接口所声明的addPerson,getPersonList方法,将会在Stub类得到实现,具体如何实现,我们展开Stub类:

[java]  view plain copy
print ?
  1. public static abstract class Stub extends android.os.Binder implements com.chenyu.service.IMyAidl {  
  2.         private static final java.lang.String DESCRIPTOR = "com.chenyu.service.IMyAidl";  
  3.   
  4.         /** 
  5.          * Construct the stub at attach it to the interface. 
  6.          */  
  7.         public Stub() {<span style="white-space:pre">     </span>//①  
  8.             this.attachInterface(this, DESCRIPTOR);  
  9.         }  
  10.   
  11.         /** 
  12.          * Cast an IBinder object into an com.chenyu.service.IMyAidl interface, 
  13.          * generating a proxy if needed. 
  14.          */  
  15.         public static com.chenyu.service.IMyAidl asInterface(android.os.IBinder obj) {    //②  
  16.             if ((obj == null)) {  
  17.                 return null;  
  18.             }  
  19.             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  20.             if (((iin != null) && (iin instanceof com.chenyu.service.IMyAidl))) {  
  21.                 return ((com.chenyu.service.IMyAidl) iin);  
  22.             }  
  23.             return new com.chenyu.service.IMyAidl.Stub.Proxy(obj);  
  24.         }  
  25.   
  26.         @Override  
  27.         public android.os.IBinder asBinder() {<span style="white-space:pre">      </span>//③  
  28.             return this;  
  29.         }  
  30.   
  31.         @Override  
  32.         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>//④  
  33.             switch (code) {  
  34.                 case INTERFACE_TRANSACTION: {  
  35.                     reply.writeString(DESCRIPTOR);  
  36.                     return true;  
  37.                 }  
  38.                 case TRANSACTION_addPerson: {  
  39.                     data.enforceInterface(DESCRIPTOR);  
  40.                     com.chenyu.service.Person _arg0;  
  41.                     if ((0 != data.readInt())) {  
  42.                         _arg0 = com.chenyu.service.Person.CREATOR.createFromParcel(data);  
  43.                     } else {  
  44.                         _arg0 = null;  
  45.                     }  
  46.                     this.addPerson(_arg0);  
  47.                     reply.writeNoException();  
  48.                     return true;  
  49.                 }  
  50.                 case TRANSACTION_getPersonList: {  
  51.                     data.enforceInterface(DESCRIPTOR);  
  52.                     java.util.List<com.chenyu.service.Person> _result = this.getPersonList();  
  53.                     reply.writeNoException();  
  54.                     reply.writeTypedList(_result);  
  55.                     return true;  
  56.                 }  
  57.             }  
  58.             return super.onTransact(code, data, reply, flags);  
  59.         }  
  60.   
  61.         private static class Proxy implements com.chenyu.service.IMyAidl {<span style="white-space:pre">  </span>//⑤  
  62.             ...  
  63.         }  
  64.   
  65.         static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); //⑥  
  66.         static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
  67.     }  
(3)从上往下地,我们逐个分析一下各个方法或者变量的作用:

①Stub()构造方法:此方法调用了父类Binder的attachInterface()方法,将当前的Interface与Binder联系起来,由于传递了DESCRIPTOR这个参数,唯一标识了当前Interface。


②asInterface(IBinder obj) :静态方法,传递了一个接口对象,该对象从哪里传递进来的呢?我们来看看上一章博客的客户端代码:

[java]  view plain copy
print ?
  1. public void onServiceConnected(ComponentName name, IBinder service) {  
  2.             Log.d("cylog""onServiceConnected success");  
  3.             iMyAidl=IMyAidl.Stub.asInterface(service);  
  4.   
  5.         }  
在这里,可以看到,调用了IMyAidl.Stub.asInterface(service)方法,即上面的②号方法,并且把service传递了进去,我们接着往下看:

[java]  view plain copy
print ?
  1. public static com.chenyu.service.IMyAidl asInterface(android.os.IBinder obj) {  
  2.             if ((obj == null)) {  
  3.                 return null;  
  4.             }  
  5.             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  6.             if (((iin != null) && (iin instanceof com.chenyu.service.IMyAidl))) {  
  7.                 return ((com.chenyu.service.IMyAidl) iin);  
  8.             }  
  9.             return new com.chenyu.service.IMyAidl.Stub.Proxy(obj);  
  10.         }  
首先判断obj是否有效,如果无效直接返回Null,说明客户端与服务端的连接失败了。接着调用了obj.queryLocalInterface(DESCRIPTOR)方法,为IInterface的对象赋值,注意到这里再次传递了DESCRIPTOR参数,可以猜测,这个方法应该是查找与当前Interface相关的一个方法,我们看看IBinder接口的queryLocalInterface()方法:

[java]  view plain copy
print ?
  1. /** 
  2.      * Attempt to retrieve a local implementation of an interface 
  3.      * for this Binder object.  If null is returned, you will need 
  4.      * to instantiate a proxy class to marshall calls through 
  5.      * the transact() method. 
  6.      */  
  7.     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()为例分析一下,提取其主要函数体如下:

[java]  view plain copy
print ?
  1. case TRANSACTION_addPerson: {  
  2.                     data.enforceInterface(DESCRIPTOR);  
  3.                     com.chenyu.service.Person _arg0;  
  4.                     if ((0 != data.readInt())) {  
  5.                         _arg0 = com.chenyu.service.Person.CREATOR.createFromParcel(data);  
  6.                     } else {  
  7.                         _arg0 = null;  
  8.                     }  
  9.                     this.addPerson(_arg0);  
  10.                     reply.writeNoException();  
  11.                     return true;  
  12.                 }  
首先声明了_arg0是Person类的对象,接着,以data为参数调用了Person类的CREATOR.createFromParcel方法,反序列化生成了Person类,这也是为什么实现了Parcelable接口的类应该同时实现CREATOR,原来在这里调用了反序列化的方法。接着,调用this.addPerson(_arg0)方法,注意:这里的this代表当前的Binder对象,那么由Binder调用的addPerson(_arg0)方法,实际上是由绑定到Binder的service调用的,即服务端调用了自身的addPerson方法。为了方便明白,让我们来回顾一下上一篇文章服务端的代码:

[java]  view plain copy
print ?
  1. private IBinder iBinder= new IMyAidl.Stub() {  
  2.         @Override  
  3.         public void addPerson(Person person) throws RemoteException {  
  4.             persons.add(person);  
  5.         }  
  6.   
  7.         @Override  
  8.         public List<Person> getPersonList() throws RemoteException {  
  9.             return persons;  
  10.         }  
  11.     };  
是不是一下子就明白了?IMyAidl.Stub()实现的接口,其中的方法在服务端得到了实现:addPerson和getPersonList()。当在Binder线程池中,调用了this.addPerson()方法,实际上回调了服务端的addPerson方法,而底层到底是怎么实现的,限于笔者的水平,暂时不了解,等以后笔者再深入了解Binder的工作机制再回答这个问题。

好了,回到当前的类,我们继续往下看:
⑤private static class Proxy:这里又出现了一个私有的静态内部类,关于这个类将在接下来详细讲述。


⑥最后两行代码分别是两个常量,标志了两个方法,即上面提到的code值。


(4)Proxy类,也实现了IMyAidl接口,同时实现了addPerson和getPersonList的方法。而Proxy类在哪里被实例化的呢?是上面(3)②中,当客户端与服务端不在同一个进程的时候,就会实例化这个代理类,并返回给客户端。什么叫做代理类呢?所谓代理,即一个中介,客户端拿到的实例,能操作服务端的部分功能,让客户端以为自己已经拿到了服务端的实例,其实不是,只是拿到服务端的一个代理而已。接下来我们展开该类,看看内部:

[java]  view plain copy
print ?
  1. private static class Proxy implements com.chenyu.service.IMyAidl {  
  2.             private android.os.IBinder mRemote;  
  3.   
  4.             Proxy(android.os.IBinder remote) {  
  5.                 mRemote = remote;  
  6.             }  
  7.   
  8.             @Override  
  9.             public android.os.IBinder asBinder() {  
  10.                 return mRemote;  
  11.             }  
  12.   
  13.             public java.lang.String getInterfaceDescriptor() {  
  14.                 return DESCRIPTOR;  
  15.             }  
  16.   
  17.             @Override  
  18.             public void addPerson(com.chenyu.service.Person person) throws android.os.RemoteException {  
  19.                 android.os.Parcel _data = android.os.Parcel.obtain();  
  20.                 android.os.Parcel _reply = android.os.Parcel.obtain();  
  21.                 try {  
  22.                     _data.writeInterfaceToken(DESCRIPTOR);  
  23.                     if ((person != null)) {  
  24.                         _data.writeInt(1);  
  25.                         person.writeToParcel(_data, 0);  
  26.                     } else {  
  27.                         _data.writeInt(0);  
  28.                     }  
  29.                     mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);  
  30.                     _reply.readException();  
  31.                 } finally {  
  32.                     _reply.recycle();  
  33.                     _data.recycle();  
  34.                 }  
  35.             }  
  36.   
  37.             @Override  
  38.             public java.util.List<com.chenyu.service.Person> getPersonList() throws android.os.RemoteException {  
  39.                 android.os.Parcel _data = android.os.Parcel.obtain();  
  40.                 android.os.Parcel _reply = android.os.Parcel.obtain();  
  41.                 java.util.List<com.chenyu.service.Person> _result;  
  42.                 try {  
  43.                     _data.writeInterfaceToken(DESCRIPTOR);  
  44.                     mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);<span style="white-space:pre"> </span>   //①  
  45.                     _reply.readException();  
  46.                     _result = _reply.createTypedArrayList(com.chenyu.service.Person.CREATOR);  
  47.                 } finally {  
  48.                     _reply.recycle();  
  49.                     _data.recycle();  
  50.                 }  
  51.                 return _result;  
  52.             }  
  53.         }  
这里关注两个接口方法的实现: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机制(一):序列化与反序列化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075115

相关文章

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

时间服务器中,适用于国内的 NTP 服务器地址,可用于时间同步或 Android 加速 GPS 定位

NTP 是什么?   NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。 NTP 实现什么目的?   目的很简单,就是为了提供准确时间。因为我们的手表、设备等,经常会时间跑着跑着就有误差,或快或慢的少几秒,时间长了甚至误差过分钟。 NTP 服务器列表 最常见、熟知的就是 www.pool.ntp.org/zo

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

Spring中事务的传播机制

一、前言 首先事务传播机制解决了什么问题 Spring 事务传播机制是包含多个事务的方法在相互调用时,事务是如何在这些方法间传播的。 事务的传播级别有 7 个,支持当前事务的:REQUIRED、SUPPORTS、MANDATORY; 不支持当前事务的:REQUIRES_NEW、NOT_SUPPORTED、NEVER,以及嵌套事务 NESTED,其中 REQUIRED 是默认的事务传播级别。