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

 Message.aidl

  com.example.aidlservicedemo.domain.Message.java

 Message.java

  com.example.aidlservicedemo.domain.User.aidl

 User.aidl

  com.example.aidlservicedemo.domain.User.java

 User.java

  服務:

  com.example.aidlservicedemo.

 CustomTypeService.java

  客戶端:

  com.example.aidlClientdemo.

 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