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()+"\n");
 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等),它們其實內部已經實現了序列化,所以無需我們再去指定序列化規則。但是對於復雜類型對象而言,系統無法知道如何去序列化與反序列化,所以需要我們指定規則。

 

  源碼下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM