最近在拜讀任主席的Android開發藝術探索,現在看了一半,再回頭看前面的,感覺跟沒有看一樣,所以還是把知識點總結一下吧,這一節咱們來講一下IPC,即進程間通信
開啓多進程模式
在Android中使用多進程只有一種方法:給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidManifest中指定android:process屬性,除此之外沒有其他辦法(除去通過JNI在native層去fork):
- android:process=”:remote”,這種方式的標記的進程名爲包名:remote,“:“的含義是指要在當前的進程名前附加包名,其次,進程名以“:”開頭的進程屬於當前應用的私有進程,其他應用的組件不可以和它跑在同一個進程中。
- android:process=”com.example.wpp.remote”,這種聲明方式是完整的命名方式,不會附件包名信息,屬於全局進程,其他應用通過ShareUID方式可以和它跑在同一個進程中
Android系統會爲每個應用分配一個唯一的UID,具有相同UID的應用才能共享數據,而兩個應用通過ShareUID跑在同一個進程需要這兩個應用有相同的ShareUID並且簽名相同纔可以,在這種情況下,它們可以互相 訪問對方的私有數據,比如data目錄、組件信息等,不管它們是否泡在同一個進程中。當然如果它們跑在同一個進程中,那麼除了能共享data目錄、組件信息,還可以共享內存數據
使用多進程會造成如下幾方面的問題:
1. 靜態成員和單例模式完全失效
2. 線程同步機制完全失效。
3. SharedPreferences的可靠性下降
4. Application會多次創建。
IPC中的一些基本概念
本節主要介紹IPC中的一些基本概念:Serializable接口、Parcelable接口以及Binder
Serializable接口
Serializable是java所提供的一個序列化接口, 是一個空接口,爲對象提供標準的序列化和反序列化操作,使用時需要聲明實現Serializable接口,並指定一個類似下面的標識即可自動實現默認的序列化過程。
private static final long serialVersionUID = 8711368828010083048L
這個serialVersionUID是用來輔助序列化和反序列化過程的,原則上序列化後的數據中的serialVersionUID只有和當前類的serialVersionUID相同才能夠正常的被反序列化。反序列化的時候會從待反序列的文件中讀取這個值和目標類中的值比對,如果不一致就會反序列化失敗。這個值可以手動指定也可以讓IDE通過當前類的結構自動去生成它的hash值,如果通過IDE自動生成,反序列化時當前類有所改變,那麼系統會重新計算serialVersionUID的值,於是反序列化失敗。當我們手動指定了它以後,可以在很大程度上避免反序列化過程的失敗。
注意:如果類結構發生了非常規性改變,比如修改了類名,修改成員變量類型,儘管serialVersionUID相同,此時反序列化還是會失敗,程序crash
另外,系統的默認序列化過程也是可以改變的,通過重寫系統writeObject和readObject方法即可。
Parcelable接口
Parcelable接口是Android提供的新的序列化方式,Parcelable也是一個接口,下面的示例是一個典型的用法。
public class User implements Parcelable{
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User(int userId, String userName, boolean isMale){
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
public int describeContents(){
return 0;
}
public void writeToParcel(Parcel out, int flags){
out.writeInt(userId);
out.wirteString(userName);
out.wirteInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
public User createFromParcel(Parcel in){
return new User(in);
}
public User[] new Array(int size){
return new User[size];
}
};
private User(Parcel in){
userId = in.readInt();
userName = in.readString();
isMale = in.readInt()==1;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}
Parcelable方法說明
方法 | 功能 | 標記位 |
---|---|---|
createFromParcel(Parcel in) | 從序列化後的對象中創建原對象 | |
new Array(int size) | 創建指定長度的原始對象數組 | |
User(Parcel in) | 從序列化後的對象中創建原始對象 | |
writeToParcel(Parcel out, int flag) | 將當前對象寫入序列化結構中,其中flag標識有兩種值:0或者1.爲1時標識當前對象需要作爲返回值返回,不能立即釋放資源,幾乎所有情況都爲0 | PARCELABLE_WRITE_RETURN_VALUE |
describeContents | 返回當前對象的內容描述,如果還有文件描述符,返回1,否則返回0,幾乎所有情況都返回0 |
系統已經爲我們提供了許多實現了Parcelable接口的類,它們都是可以直接序列化的,比如Intent、Bundle、Bitmap等,同時List和Map也可以序列化,前提是它們裏面每個元素都是可序列化的。
Parcelable和Serializable區別:
Serializable是java中的序列化接口,其使用起來簡單但是開銷大,序列化和反序列化需要大量I/O操作。而Parcelable是Android中的序列化方式,因此更適合用在Android平臺上,缺點就是用起來稍微麻煩點,但是它的效率高,這是Android推薦的序列化方式。