前言
Android的AIDL不僅可以在綁定服務中傳遞一些Android規定的數據類型的數據,還可以傳遞一些復雜類型的數據。但是與傳遞系統允許的數據類型相比,復雜類型數據的傳遞要做更多的工作,本篇博客就講解一下如何使用AIDL接口通過綁定服務在進程間傳遞數據。關於AIDL傳遞規定類型數據的內容,不了解的朋友可以看看之前的博客: AIDL傳遞系統允許類型數據。
本篇博客的主要內容:
前面已經介紹了通過AIDL接口在進程間傳遞系統允許的數據,如果需要傳遞一個復雜類型的對象,就沒那么簡單了,需要額外做一些處理。如下:
- 定義數據接口的AIDL文件中,使用parcelable關鍵字,例如:parcelable Message;
- 在其數據實現類中實現Parcelable接口,並實現對應的方法。
- 在業務接口的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 }
先來說說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傳遞數據等其他地方,這不是本篇博客的主題,以后用到再詳細介紹。
定義好數據接口的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()方法的順序必須一致,因為一般序列化數據是以鏈的形式序列化的,如果順序不對,反序列化的數據會出錯。
關鍵點已經講到, 下面通過一個簡單的Demo來演示AIDL傳遞復雜對象。
AIDL接口:
com.example.aidlservicedemo.domain.Message.aidl

parcelable Message;
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 }
com.example.aidlservicedemo.domain.User.aidl

1 parcelable User;
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 }
服務:
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 }
客戶端:
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 }
效果展示:
通過上面Demo打印的日志,解釋一下序列化的過程,打開Logcat查看日志。
從上圖的PID列可以看出這是兩個線程間的交互。
流程是這樣的,客戶端傳遞一個User對象給服務端,服務端通過User對象處理數據,返回兩個Message對象給客戶端。
首先,在客戶端傳遞給服務端User對象前,客戶端先把User對象序列化,然后傳遞給服務端之后,服務端接收到的是一段序列化后的數據,它再按照原定的規則對數據進行反序列化,然后處理User。當服務端查到這個User有兩條Message時,需要傳遞這兩條Message對象給客戶端,在傳遞前對Message對象進行序列化,客戶端收到服務端傳遞過來的序列化后的數據,再根據既定的規則進行反序列化,得到正確的對象。
從這個流程可以看出,在進程間傳遞的數據必定是被序列化過的,否則無法傳遞。而對於那些AIDL默認允許傳遞的數據類型(int、double、String、List等),它們其實內部已經實現了序列化,所以無需我們再去指定序列化規則。但是對於復雜類型對象而言,系統無法知道如何去序列化與反序列化,所以需要我們指定規則。