Android--Service之AIDL传递复杂对象

前言

  Android的AIDL不仅可以在绑定服务中传递一些Android规定的数据类型的数据,还可以传递一些复杂类型的数据。但是与传递系统允许的数据类型相比,复杂类型数据的传递要做更多的工作,本篇博客就讲解一下如何使用AIDL接口通过绑定服务在进程间传递数据。关于AIDL传递规定类型数据的内容,不了解的朋友可以看看之前的博客: AIDL传递系统允许类型数据

  本篇博客的主要内容:

  1. AIDL传递复杂类型对象的特殊处理
  2. Parcelable与Parcel接口
  3. 实现Parcelable接口
  4. AIDL传递复杂类型对象Demo
  5. AIDL传递对象序列化过程详解

AIDL传递复杂类型对象的特殊处理 

  前面已经介绍了通过AIDL接口在进程间传递系统允许的数据,如果需要传递一个复杂类型的对象,就没那么简单了,需要额外做一些处理。如下:

  1. 定义数据接口的AIDL文件中,使用parcelable关键字,例如:parcelable Message;
  2. 在其数据实现类中实现Parcelable接口,并实现对应的方法。
  3. 在业务接口的AIDL文件中,使用import引入数据接口AIDL的包名。

  例如:Message.aidl

1 parcelable Message; 

  例如:IGetMsg.aidl

 1 package com.example.aidlservicedemo.domain;
 2 
 3 // 这是两个自定义类
 4 import com.example.aidlservicedemo.domain.Message;
 5 import com.example.aidlservicedemo.domain.User;
 6 
 7 interface IGetMsg{
 8     // 在AIDL接口中定义一个getMes方法
 9     List<Message> getMes(in User us);
10 }

Parcelable与Parcel接口

  先来说说Android对象序列化,在Android中序列化对象主要有两种方式,实现Serializable接口或是实现Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高,而AIDL进行在进程间通信(IPC),就是需要实现这个Parcelable接口。

  Parcelable接口的作用:实现了Parcelable接口的实例,可以将自身的数据信息写入一个Parcel对象,也可以从parcel中恢复到对象的状态。而Parcel就是完成数据序列化写入的载体。

  上面提到Parcel,再来聊聊Parcel是什么?Android系统设计之初,定位就是针对内存受限的设备,因此对性能要求更好,所以系统中采用进程间通信(IPC)机制,必然要求性能更优良的序列化方式,所以Parcel就被设计出来了,其定位就是轻量级的高效的对象序列化机制与反序列化机制。如果读一下Android的底层代码,会发现Parcel是使用C++实现的,底层直接通过Parcel指针操作内存实现,所以它的才更高效。

  Parcel也提供了一系列的方法帮助写入数据与读取数据,这里简单介绍一下:

  • obtain():在池中获取一个新的Parcel。
  • dataSize():得到当前Parcel对象的实际存储空间。
  • dataPostion():获取当前Parcel对象的偏移量。
  • setDataPosition():设置当前Parcel对象的偏移量。
  • recyle():清空、回收当前Parcel对象的内存。
  • writeXxx():向当前Parcel对象写入数据,具有多种重载。
  • readXxx():从当前Parcel对象读取数据,具有多种重载。

  简单来说,Parcelable通过writeToParcel()方法,对复杂对象的数据写入Parcel的方式进行对象序列化,然后在需要的时候,通过其内定义的静态属性CREATOR.createFromParcel()进行反序列化的操作。Parcelable对Parcel进行了包装,其内部就是通过操作Parcel进行序列化与反序列化的。

  Parcelable与Parcel均定义在android.os包下,而这种机制不仅用于AIDL,还可以用于Intent传递数据等其他地方,这不是本篇博客的主题,以后用到再详细介绍。

实现Parcelable接口

  定义好数据接口的AIDL文件后,需要定义一个数据实现类,实现Parcelable接口,并实现对应的方法,Parcelable有如下几个必须要实现的抽象方法:

  • abstract int describeContents():返回一个位掩码,表示一组特殊对象类型的Parcelable,一般返回0即可。
  • asbtract void writeToParcel(Parcel dest,int flags):实现对象的序列化,通过Parcel的一系列writeXxx()方法序列化对象。

  除了上面两个方法,还需要在实现类中定义一个名为"CREATOR",类型为"Parcelable.Creator<T>"的泛型静态属性,它实现了对象的反序列化。它也有两个必须实现的抽象方法:

  • abstract T createFromParcel(Parcel source):通过source对象,根据writeToParcel()方法序列化的数据,反序列化一个Parcelable对象。
  • abstract T[] newArray(int size):创建一个新的Parcelable对象的数组。

  例如:

 1     @Override
 2     public int describeContents() {
 3         return 0;
 4     }
 5     
 6     @Override
 7     public void writeToParcel(Parcel dest, int flags) {
 8         Log.i("main", "服务端Message被序列化");
 9         dest.writeInt(id);
10         dest.writeString(msgText);
11         dest.writeString(fromName);
12         dest.writeString(date);
13     }
14 
15     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
16 
17         @Override
18         public Message[] newArray(int size) {
19             return new Message[size];
20         }
21 
22         @Override
23         public Message createFromParcel(Parcel source) {
24             Log.i("main", "服务端Message被反序列化");
25             return new Message(source.readInt(), source.readString(),
26                     source.readString(), source.readString());
27         }
28     };

  从上面示例中可以看出,使用writeToParcel()方法进行序列化,通过CREATOR.createFromParcel进行反序列化,它们都传递一个Parcel类型的对象,这里要注意的是两个方法中Parcel对象的writeXxx()和readXxx()方法的顺序必须一致,因为一般序列化数据是以链的形式序列化的,如果顺序不对,反序列化的数据会出错。

AIDL传递复杂类型对象Demo

   关键点已经讲到, 下面通过一个简单的Demo来演示AIDL传递复杂对象。

  AIDL接口:

  com.example.aidlservicedemo.domain.Message.aidl

parcelable Message; 
Message.aidl

  com.example.aidlservicedemo.domain.Message.java

 1 package com.example.aidlservicedemo.domain;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 import android.text.format.DateUtils;
 6 import android.util.Log;
 7 
 8 public class Message implements Parcelable {
 9 
10     private int id;
11     private String msgText;
12     private String fromName;
13     private String date;
14 
15     public Message() {
16         super();
17 
18     }
19 
20     public Message(int id, String msgText, String fromName, String date) {
21         super();
22         this.id = id;
23         this.msgText = msgText;
24         this.fromName = fromName;
25         this.date = date;
26     }
27 
28     public int getId() {
29         return id;
30     }
31 
32     public void setId(int id) {
33         this.id = id;
34     }
35 
36     public String getMsgText() {
37         return msgText;
38     }
39 
40     public void setMsgText(String msgText) {
41         this.msgText = msgText;
42     }
43 
44     public String getFromName() {
45         return fromName;
46     }
47 
48     public void setFromName(String fromName) {
49         this.fromName = fromName;
50     }
51 
52     public String getDate() {
53         return date;
54     }
55 
56     public void setDate(String date) {
57         this.date = date;
58     }
59 
60     @Override
61     public int describeContents() {
62         return 0;
63     }
64 
65     @Override
66     public String toString() {
67         return "信息内容=" + msgText + ", 发件人="
68                 + fromName + ", 时间=" + date ;
69     }
70 
71     @Override
72     public void writeToParcel(Parcel dest, int flags) {
73         Log.i("main", "客户端Message被序列化");
74         dest.writeInt(id);
75         dest.writeString(msgText);
76         dest.writeString(fromName);
77         dest.writeString(date);
78     }
79 
80     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
81 
82         @Override
83         public Message[] newArray(int size) {
84             return new Message[size];
85         }
86 
87         @Override
88         public Message createFromParcel(Parcel source) {
89             Log.i("main", "客户端Message被反序列化");
90             return new Message(source.readInt(), source.readString(),
91                     source.readString(), source.readString());
92         }
93     };
94 }
Message.java

  com.example.aidlservicedemo.domain.User.aidl

1 parcelable User; 
User.aidl

  com.example.aidlservicedemo.domain.User.java

 1 package com.example.aidlservicedemo.domain;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 import android.util.Log;
 6 
 7 public class User implements Parcelable {
 8     
 9     
10     private int id;
11     private String username;
12     private String password;
13     public User() {
14         super();
15     }
16     public User(int id, String username, String password) {
17         super();
18         this.id = id;
19         this.username = username;
20         this.password = password;
21     }
22 
23     public int getId() {
24         return id;
25     }
26 
27     public void setId(int id) {
28         this.id = id;
29     }
30 
31     public String getUsername() {
32         return username;
33     }
34 
35     public void setUsername(String username) {
36         this.username = username;
37     }
38 
39     public String getPassword() {
40         return password;
41     }
42 
43     public void setPassword(String password) {
44         this.password = password;
45     }
46 
47     @Override
48     public boolean equals(Object o) {
49 
50         User us=(User)o;
51         if(this.username.equals(us.username)&&this.password.equals(us.password))
52         {
53             return true;
54         }
55         else
56         {
57             return false;
58         }
59     }
60     
61     @Override
62     public int describeContents() {
63         return 0;
64     }
65 
66     @Override
67     public void writeToParcel(Parcel dest, int flags) {
68         Log.i("main", "客户端User被序列化");
69         dest.writeInt(id);
70         dest.writeString(username);
71         dest.writeString(password);
72     }
73     public static final Parcelable.Creator<User> CREATOR=new Creator<User>() {
74 
75         @Override
76         public User[] newArray(int size) {
77             return new User[size];
78         }
79 
80         @Override
81         public User createFromParcel(Parcel source) {
82             Log.i("main", "客户端User被反序列化");
83             return new User(source.readInt(), source.readString(),
84                     source.readString());
85         }
86     };
87 }
User.java

  服务:

  com.example.aidlservicedemo.

 1 package com.example.aidlservicedemo;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Date;
 5 import java.util.HashMap;
 6 import java.util.List;
 7 import java.util.Map;
 8 
 9 import com.example.aidlservicedemo.domain.IGetMsg.Stub;
10 import com.example.aidlservicedemo.domain.Message;
11 import com.example.aidlservicedemo.domain.User;
12 
13 import android.app.Service;
14 import android.content.Intent;
15 import android.os.IBinder;
16 import android.os.RemoteException;
17 import android.util.Log;
18 
19 public class CustomTypeService extends Service {
20     private static final String TAG = "main";
21     private MsgBinder msgBinder=null;
22     private static Map<User, List<Message>> map=new HashMap<User, List<Message>>();
23     static{
24         for(int i=0;i<3;i++){
25             User user=new User(i, "jack"+i, "9999999999"+i);
26             List<Message> messages=new ArrayList<Message>();
27             Message msg=null;
28             if(i==0)
29             {
30                 msg=new Message(i, "这两天好吗?", "Jerry", new Date().toGMTString());
31                 messages.add(msg);
32             }else if(i==1)
33             {
34                 msg=new Message(i, "周天去逛街吧!", "Tim", new Date().toGMTString());
35                 messages.add(msg);
36                 msg=new Message(i, "好无聊!", "Wesley", new Date().toGMTString());
37                 messages.add(msg);
38             }
39             else
40             {
41                 msg=new Message(i, "上次的问题解决了吗?", "Bonnie", new Date().toGMTString());
42                 messages.add(msg);
43                 msg=new Message(i, "明天一起吃饭吧?", "Matt", new Date().toGMTString());
44                 messages.add(msg);
45                 msg=new Message(i, "在哪里?", "Caroline", new Date().toGMTString());
46                 messages.add(msg);
47             }
48             map.put(user, messages);
49         }
50     }
51     
52     public class MsgBinder extends Stub{
53 
54         @Override
55         public List<Message> getMes(User us) throws RemoteException {            
56             for(Map.Entry<User, List<Message>> msgs:map.entrySet()){
57                 if(msgs.getKey().getUsername().equals(us.getUsername())&&msgs.getKey().getPassword().equals(us.getPassword())){
58                     Log.i(TAG, "找到信息了");
59                     return msgs.getValue();
60                 }
61             }
62             Log.i(TAG, "没找到信息了");
63             return map.get(us);
64         }
65         
66     }
67     
68     @Override
69     public IBinder onBind(Intent intent) {
70         // TODO Auto-generated method stub
71         return msgBinder;
72     }
73 
74     @Override
75     public void onCreate() {
76         // TODO Auto-generated method stub
77         super.onCreate();
78         msgBinder=new MsgBinder();
79     }
80 
81     @Override
82     public void onDestroy() {
83         msgBinder=null;
84         super.onDestroy();
85     }
86 
87 }
CustomTypeService.java

  客户端:

  com.example.aidlClientdemo.

  1 package com.example.aidlClientdemo;
  2 
  3 import java.util.List;
  4 import java.util.Random;
  5 
  6 import com.example.aidlservicedemo.domain.IGetMsg;
  7 import com.example.aidlservicedemo.domain.Message;
  8 import com.example.aidlservicedemo.domain.User;
  9 
 10 import android.app.Activity;
 11 import android.content.ComponentName;
 12 import android.content.Intent;
 13 import android.content.ServiceConnection;
 14 import android.os.Bundle;
 15 import android.os.IBinder;
 16 import android.os.RemoteException;
 17 import android.view.View;
 18 import android.view.View.OnClickListener;
 19 import android.widget.Button;
 20 import android.widget.Toast;
 21 
 22 public class CustomTypeActivity extends Activity {
 23     private Button btn_startService, btn_endService, btn_getServiceData;
 24     private IGetMsg getMsg;
 25 
 26     private static User[] users = new User[] {
 27             new User(0, "jack0", "99999999990"),
 28             new User(0, "jack1", "99999999991"),
 29             new User(0, "jack2", "99999999992") };
 30 
 31     private ServiceConnection conn = new ServiceConnection() {
 32 
 33         @Override
 34         public void onServiceDisconnected(ComponentName name) {
 35             getMsg = null;
 36         }
 37 
 38         @Override
 39         public void onServiceConnected(ComponentName name, IBinder service) {
 40             getMsg = IGetMsg.Stub.asInterface(service);
 41         }
 42     };
 43 
 44     @Override
 45     protected void onCreate(Bundle savedInstanceState) {
 46         super.onCreate(savedInstanceState);
 47         setContentView(R.layout.activity_service);
 48 
 49         btn_startService = (Button) findViewById(R.id.btn_startService);
 50         btn_endService = (Button) findViewById(R.id.btn_endService);
 51         btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData);
 52         btn_startService.setOnClickListener(click);
 53         btn_endService.setOnClickListener(click);
 54         btn_getServiceData.setOnClickListener(click);
 55     }
 56 
 57     private View.OnClickListener click = new OnClickListener() {
 58 
 59         @Override
 60         public void onClick(View v) {
 61 
 62             switch (v.getId()) {
 63             case R.id.btn_startService:
 64                 startService();
 65                 break;
 66             case R.id.btn_endService:
 67                 endService();
 68                 break;
 69             case R.id.btn_getServiceData:
 70                 getServiceDate();
 71                 break;
 72             }
 73 
 74         }
 75     };
 76 
 77     /**
 78      * 获取其他线程服务数据
 79      */
 80     private void getServiceDate(){
 81         try {
 82             Random random=new Random();        
 83             int nextInt=random.nextInt(2);
 84             List<Message> msgs=getMsg.getMes(users[nextInt]);
 85             StringBuilder sBuilder=new StringBuilder();
 86             for(Message msg:msgs){
 87                 sBuilder.append(msg.toString()+"
");
 88             }
 89             Toast.makeText(CustomTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show();
 90         } catch (Exception e) {
 91             Toast.makeText(CustomTypeActivity.this, "获取数据出错", Toast.LENGTH_SHORT).show();
 92             e.printStackTrace();
 93         }
 94     }
 95 
 96     /**
 97      * 开始服务
 98      */
 99     private void startService() {
100         Intent service = new Intent();
101         service.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE");
102         bindService(service, conn, BIND_AUTO_CREATE);
103         Toast.makeText(CustomTypeActivity.this, "绑定服务成功", Toast.LENGTH_SHORT).show();
104     }
105 
106     /**
107      * 停止服务
108      */
109     private void endService() {
110         unbindService(conn);
111         Toast.makeText(CustomTypeActivity.this, "解除绑定服务成功", Toast.LENGTH_SHORT).show();
112     }
113 
114 }
CustomTypeActivity.java

  效果展示:

AIDL传递对象序列化过程详解

  通过上面Demo打印的日志,解释一下序列化的过程,打开Logcat查看日志。

  从上图的PID列可以看出这是两个线程间的交互。

  流程是这样的,客户端传递一个User对象给服务端,服务端通过User对象处理数据,返回两个Message对象给客户端。

  首先,在客户端传递给服务端User对象前,客户端先把User对象序列化,然后传递给服务端之后,服务端接收到的是一段序列化后的数据,它再按照原定的规则对数据进行反序列化,然后处理User。当服务端查到这个User有两条Message时,需要传递这两条Message对象给客户端,在传递前对Message对象进行序列化,客户端收到服务端传递过来的序列化后的数据,再根据既定的规则进行反序列化,得到正确的对象。

  从这个流程可以看出,在进程间传递的数据必定是被序列化过的,否则无法传递。而对于那些AIDL默认允许传递的数据类型(int、double、String、List等),它们其实内部已经实现了序列化,所以无需我们再去指定序列化规则。但是对于复杂类型对象而言,系统无法知道如何去序列化与反序列化,所以需要我们指定规则。

  源码下载

原文地址:https://www.cnblogs.com/plokmju/p/android_Service_aidl_CustomType.html