android--service之aidl传递复杂对象,Android--Service之AIDL传递复杂对象
前言
Android的AIDL不僅可以在綁定服務(wù)中傳遞一些Android規(guī)定的數(shù)據(jù)類型的數(shù)據(jù),還可以傳遞一些復(fù)雜類型的數(shù)據(jù)。但是與傳遞系統(tǒng)允許的數(shù)據(jù)類型相比,復(fù)雜類型數(shù)據(jù)的傳遞要做更多的工作,本篇博客就講解一下如何使用AIDL接口通過綁定服務(wù)在進(jìn)程間傳遞數(shù)據(jù)。關(guān)于AIDL傳遞規(guī)定類型數(shù)據(jù)的內(nèi)容,不了解的朋友可以看看之前的博客:AIDL傳遞系統(tǒng)允許類型數(shù)據(jù)。
本篇博客的主要內(nèi)容:
AIDL傳遞復(fù)雜類型對(duì)象的特殊處理
前面已經(jīng)介紹了通過AIDL接口在進(jìn)程間傳遞系統(tǒng)允許的數(shù)據(jù),如果需要傳遞一個(gè)復(fù)雜類型的對(duì)象,就沒那么簡(jiǎn)單了,需要額外做一些處理。如下:
定義數(shù)據(jù)接口的AIDL文件中,使用parcelable關(guān)鍵字,例如:parcelable Message;
在其數(shù)據(jù)實(shí)現(xiàn)類中實(shí)現(xiàn)Parcelable接口,并實(shí)現(xiàn)對(duì)應(yīng)的方法。
在業(yè)務(wù)接口的AIDL文件中,使用import引入數(shù)據(jù)接口AIDL的包名。
例如:Message.aidl
1 parcelable Message;
例如:IGetMsg.aidl
1 package com.example.aidlservicedemo.domain;
2
3 // 這是兩個(gè)自定義類
4 import com.example.aidlservicedemo.domain.Message;
5 import com.example.aidlservicedemo.domain.User;
6
7 interface IGetMsg{
8 // 在AIDL接口中定義一個(gè)getMes方法
9 List getMes(in User us);
10 }
Parcelable與Parcel接口
先來說說Android對(duì)象序列化,在Android中序列化對(duì)象主要有兩種方式,實(shí)現(xiàn)Serializable接口或是實(shí)現(xiàn)Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高,而AIDL進(jìn)行在進(jìn)程間通信(IPC),就是需要實(shí)現(xiàn)這個(gè)Parcelable接口。
Parcelable接口的作用:實(shí)現(xiàn)了Parcelable接口的實(shí)例,可以將自身的數(shù)據(jù)信息寫入一個(gè)Parcel對(duì)象,也可以從parcel中恢復(fù)到對(duì)象的狀態(tài)。而Parcel就是完成數(shù)據(jù)序列化寫入的載體。
上面提到Parcel,再來聊聊Parcel是什么?Android系統(tǒng)設(shè)計(jì)之初,定位就是針對(duì)內(nèi)存受限的設(shè)備,因此對(duì)性能要求更好,所以系統(tǒng)中采用進(jìn)程間通信(IPC)機(jī)制,必然要求性能更優(yōu)良的序列化方式,所以Parcel就被設(shè)計(jì)出來了,其定位就是輕量級(jí)的高效的對(duì)象序列化機(jī)制與反序列化機(jī)制。如果讀一下Android的底層代碼,會(huì)發(fā)現(xiàn)Parcel是使用C++實(shí)現(xiàn)的,底層直接通過Parcel指針操作內(nèi)存實(shí)現(xiàn),所以它的才更高效。
Parcel也提供了一系列的方法幫助寫入數(shù)據(jù)與讀取數(shù)據(jù),這里簡(jiǎn)單介紹一下:
obtain():在池中獲取一個(gè)新的Parcel。
dataSize():得到當(dāng)前Parcel對(duì)象的實(shí)際存儲(chǔ)空間。
dataPostion():獲取當(dāng)前Parcel對(duì)象的偏移量。
setDataPosition():設(shè)置當(dāng)前Parcel對(duì)象的偏移量。
recyle():清空、回收當(dāng)前Parcel對(duì)象的內(nèi)存。
writeXxx():向當(dāng)前Parcel對(duì)象寫入數(shù)據(jù),具有多種重載。
readXxx():從當(dāng)前Parcel對(duì)象讀取數(shù)據(jù),具有多種重載。
簡(jiǎn)單來說,Parcelable通過writeToParcel()方法,對(duì)復(fù)雜對(duì)象的數(shù)據(jù)寫入Parcel的方式進(jìn)行對(duì)象序列化,然后在需要的時(shí)候,通過其內(nèi)定義的靜態(tài)屬性CREATOR.createFromParcel()進(jìn)行反序列化的操作。Parcelable對(duì)Parcel進(jìn)行了包裝,其內(nèi)部就是通過操作Parcel進(jìn)行序列化與反序列化的。
Parcelable與Parcel均定義在android.os包下,而這種機(jī)制不僅用于AIDL,還可以用于Intent傳遞數(shù)據(jù)等其他地方,這不是本篇博客的主題,以后用到再詳細(xì)介紹。
實(shí)現(xiàn)Parcelable接口
定義好數(shù)據(jù)接口的AIDL文件后,需要定義一個(gè)數(shù)據(jù)實(shí)現(xiàn)類,實(shí)現(xiàn)Parcelable接口,并實(shí)現(xiàn)對(duì)應(yīng)的方法,Parcelable有如下幾個(gè)必須要實(shí)現(xiàn)的抽象方法:
abstract int describeContents():返回一個(gè)位掩碼,表示一組特殊對(duì)象類型的Parcelable,一般返回0即可。
asbtract void writeToParcel(Parcel dest,int flags):實(shí)現(xiàn)對(duì)象的序列化,通過Parcel的一系列writeXxx()方法序列化對(duì)象。
除了上面兩個(gè)方法,還需要在實(shí)現(xiàn)類中定義一個(gè)名為"CREATOR",類型為"Parcelable.Creator"的泛型靜態(tài)屬性,它實(shí)現(xiàn)了對(duì)象的反序列化。它也有兩個(gè)必須實(shí)現(xiàn)的抽象方法:
abstract T createFromParcel(Parcel source):通過source對(duì)象,根據(jù)writeToParcel()方法序列化的數(shù)據(jù),反序列化一個(gè)Parcelable對(duì)象。
abstract T[] newArray(int size):創(chuàng)建一個(gè)新的Parcelable對(duì)象的數(shù)組。
例如:
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", "服務(wù)端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 CREATOR = new Creator() {
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", "服務(wù)端Message被反序列化");
25 return new Message(source.readInt(), source.readString(),
26 source.readString(), source.readString());
27 }
28 };
從上面示例中可以看出,使用writeToParcel()方法進(jìn)行序列化,通過CREATOR.createFromParcel進(jìn)行反序列化,它們都傳遞一個(gè)Parcel類型的對(duì)象,這里要注意的是兩個(gè)方法中Parcel對(duì)象的writeXxx()和readXxx()方法的順序必須一致,因?yàn)橐话阈蛄谢瘮?shù)據(jù)是以鏈的形式序列化的,如果順序不對(duì),反序列化的數(shù)據(jù)會(huì)出錯(cuò)。
AIDL傳遞復(fù)雜類型對(duì)象Demo
關(guān)鍵點(diǎn)已經(jīng)講到, 下面通過一個(gè)簡(jiǎn)單的Demo來演示AIDL傳遞復(fù)雜對(duì)象。
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
服務(wù):
com.example.aidlservicedemo.
?CustomTypeService.java
客戶端:
com.example.aidlClientdemo.
?CustomTypeActivity.java
效果展示:
AIDL傳遞對(duì)象序列化過程詳解
通過上面Demo打印的日志,解釋一下序列化的過程,打開Logcat查看日志。
從上圖的PID列可以看出這是兩個(gè)線程間的交互。
流程是這樣的,客戶端傳遞一個(gè)User對(duì)象給服務(wù)端,服務(wù)端通過User對(duì)象處理數(shù)據(jù),返回兩個(gè)Message對(duì)象給客戶端。
首先,在客戶端傳遞給服務(wù)端User對(duì)象前,客戶端先把User對(duì)象序列化,然后傳遞給服務(wù)端之后,服務(wù)端接收到的是一段序列化后的數(shù)據(jù),它再按照原定的規(guī)則對(duì)數(shù)據(jù)進(jìn)行反序列化,然后處理User。當(dāng)服務(wù)端查到這個(gè)User有兩條Message時(shí),需要傳遞這兩條Message對(duì)象給客戶端,在傳遞前對(duì)Message對(duì)象進(jìn)行序列化,客戶端收到服務(wù)端傳遞過來的序列化后的數(shù)據(jù),再根據(jù)既定的規(guī)則進(jìn)行反序列化,得到正確的對(duì)象。
從這個(gè)流程可以看出,在進(jìn)程間傳遞的數(shù)據(jù)必定是被序列化過的,否則無法傳遞。而對(duì)于那些AIDL默認(rèn)允許傳遞的數(shù)據(jù)類型(int、double、String、List等),它們其實(shí)內(nèi)部已經(jīng)實(shí)現(xiàn)了序列化,所以無需我們?cè)偃ブ付ㄐ蛄谢?guī)則。但是對(duì)于復(fù)雜類型對(duì)象而言,系統(tǒng)無法知道如何去序列化與反序列化,所以需要我們指定規(guī)則。
總結(jié)
以上是生活随笔為你收集整理的android--service之aidl传递复杂对象,Android--Service之AIDL传递复杂对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 加载更多,如何在andr
- 下一篇: android 新浪微博的点赞功能实现,