Android如何通過parcelable實現跨程序之間多型的型別的傳遞。
一個後臺服務,提供一個介面,想要利用上層的應用傳遞下來的資料,進行處理。但是這類資料種類繁多,該怎麼辦呢?Android 提供的parcelable將這些資料傳遞給世界的另一邊。之前看了網路上的做法,有一篇文章無限接近實現了,但是由於部分錯誤,導致了我一直是失敗的,後來自己經過摸索才終於找到原因。廢話不多說,講講乾貨。
假設我們在服務端有多個數據Bean,如ABean, BBean,CBean等待傳輸,那麼就讓他們愉快的去繼承一個RootBean的抽象類,記住這裡的父類一定要是抽象類,否則後面會讓你生不如死。多型的核心就是這個抽象類(這裡千萬不要去實現Creator交給他的兒子吧)
import android.os.Parcel; import android.os.Parcelable; public abstract class RootBean implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { } protected RootData(Parcel in) { } protected RootData() { } }
看看ABean是怎麼繼承它爸爸的
import android.os.Parcel; public class ABean extends RootBean { int childData; public int getChildData() { return childData; } public void setChildData(int childData) { this.childData = childData; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(this.childData); } public ChildData() { super(); } protected ChildData(Parcel in) { super(in); this.childData = in.readInt(); } public static final Creator<ChildData> CREATOR = new Creator<ChildData>() { public ChildData createFromParcel(Parcel in) { return new ChildData(in); } public ChildData[] newArray(int size) { return new ChildData[size]; } }; }
很簡單,沒有什麼難度,按照通用的parcelable的自動生成方式就可以了。
伺服器端提供了一個,且有且僅有一個介面傳遞資料,這個介面的引數只能有一個,按照上面的設計思路,這裡我們想傳一個父類(RootBean)到世界的盡頭,在世界的盡頭根據"多型"再來動態生成相應的兒子物件。
為了實現上面的想法,我們這裡需要一個Wrapper類來包裝下自己,以便於自己能夠被傳輸出去。
這裡的關鍵就是public class DataWrapper implements Parcelable { private RootBea rootBean; public RootData getRootBean() { return rootBean; } public void setRootBean(RootData rootBean) { this.rootBean= rootDean; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(this.rootData, flags); } public DataWrapper() { } protected DataWrapper(Parcel in) { this.rootBean = in.readParcelable(RootData.class.getClassLoader());//關鍵 } public static final Creator<DataWrapper> CREATOR = new Creator<DataWrapper>() { @Override public DataWrapper createFromParcel(Parcel source) { return new DataWrapper(source); } @Override public DataWrapper[] newArray(int size) { return new DataWrapper[size]; } }; }
this.rootBean = in.readParcelable(RootBean.class.getClassLoader());
這個是parcelable獲取物件的類,如果沒有這句什麼也白搭,如果父類不是抽象,這裡也沒用。
有人會問,如果這個資料到了上面,我怎麼判斷到底是啥型別的資料,這裡可以在這個Wrapper類裡面加個屬性比如type或者啥的,具體怎麼寫就不在這裡描述了,很簡單,和普通的parcelable加一個屬性一樣。
下面講講怎麼傳送這個資料吧。
由於需要傳遞一個物件型別的資料而非通用型別的資料,這裡需要宣告一個和DataWrapper.java類同名的一個物件AIDL檔案
// IDataWrapper.aidl
package com.hxcode.polymorphism;
// Declare any non-default types here with import statements
parcelable DataWrapper;
注意包名和DataWrapper.java要一致,注意包名要一致,注意包名要一致,重要的事情說三遍。
下面就是後臺服務定義的一個討要資料的介面了(我不是要飯的)
package com.hxcode.polymorphism;
// Declare any non-default types here with import statements
import com.hxcode.polymorphism.DataWrapper;
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void getData(in DataWrapper data);
}
這個介面定義了,那就需要實現他public class ServiceImpl extends IRemoteService.Stub {
private final static String TAG = "ServiceImpl";
@Override
public void sendData(DataWrapper dataWrapper) throws RemoteException {
ABean childData = (ABean) dataWrapper.getRootBean();
int data = childData.getChildData();//get child data
Log.d(TAG, "send data is:"+data);
}
}
如何呼叫定義的這個AIDL定義的介面?
這裡不再寫如何實現的,無非就是先繫結service,然後再獲取IBinder等等標準的做法。
這裡需要提到一點的是,有的時候,有些資料型別的有些屬性是一致的,假設ABean, BBean, CBean都有名稱name和地址address這些,那麼我們可以再抽出一個父類。繼承關係如下: