1. 程式人生 > >Android開發——程序間通訊之Bundle和檔案

Android開發——程序間通訊之Bundle和檔案

0.  前言

不論是Android還是其他作業系統,都會有自己的IPC機制,所謂IPCInter-Process Communication)即程序間通訊。首先執行緒和程序是很不同的概念,執行緒是CPU呼叫的最小單元,程序一般在PC和移動裝置上指一個程式或者一個應用,一個程序可以包含多個執行緒。Android一個應用中可以通過android:process屬性開啟多程序模式,用於某些模組必須執行在單獨程序中或為一個應用多申請點記憶體。關於多程序的使用場景,詳情可以參考

該屬性若以“:”開頭則該程序屬於當前應用的私有程序,否則屬於全域性程序

雖然名為私有程序,但是可以為其再宣告一個android:exported="true"

屬性,然後增加intent-filter屬性(如果沒有intent-filter屬性,只能在本應用內使用),其他應用也可以關聯到這個程序

全域性程序中假設有多個應用都有聲明瞭android:process="com.XX"全域性程序的元件,會產生多個名字相同但PID並不相同的程序所以全域性程序並非全域性唯一的意思。全域性程序的作用是,其他應用可以通過ShareUID的方式和這個全域性程序跑在同一個程序上,從而共享資源。

IPC方式有很多,在Android中常用的IPC方式包括Bundle、檔案、MessengerAIDLContentProviderSocket等方式。

本篇主要講解使用Bundle

和檔案的方式。本文原創,轉載請註明出處為SEU_Calvin的部落格

1.  Bundle

ActivityServiceReceiver都是支援通過Intent傳遞Bundle資料的,由於Bundle實現了Parcelable介面,所以它可以方便的在不同的程序中傳輸。因此當我們在一個程序中啟動了另外一個程序的ActivityServiceReceiver,我們就可以在Bundle中附加我們需要傳輸給遠端程序的資訊(前提是能夠被序列化)並通過Intent傳送出去

下面是一個簡單的示例,在一個應用中的MainActivityBundleActivity(分屬不同程序)之間使用Bundle

傳輸資料。首先在MainActivity中使用Bundle包裝我們的字串資料。

Bundle bundle = new Bundle();  
bundle.putString("ipc", "Bundle test");  
Intent intent=new Intent(MainActivity.this,BundleActivity.class);  
intent.putExtras(bundle);  
startActivity(intent);

並在BundleActivity中進行資料接收,其中“ipc”作為某個Bundle資料單元的標識。

Bundle bundle=getIntent().getExtras();  
//獲取Bundle的資訊  
String info=bundle.getString("ipc");

2.  檔案

2.1  思想

兩個程序通過讀寫同一個檔案來交換資料,利用這個思想,我們可以序列化一個物件到檔案系統中,同時在另一個程序中反序列化恢復這個物件。

共享資料對檔案格式沒有具體要求,可以是文字檔案,也可以是xml檔案等等,主要注意處理併發讀寫的問題。

SharePreferences在底層實現上就是採用xml檔案來儲存鍵值對,但是SharePreferences的快取策略使其在記憶體中會有一份快取,因此在多程序模式下,系統對它的讀寫就變得不可靠。因此需要格外注意使用SharePreferences進行IPC

2.2  transient

一個物件只要實現了Serilizable介面,這個物件就可以被序列化,但是一個類的有些屬性需要序列化,而其他屬性不需要被序列化(一個靜態變數不管是否被transient修飾,均不能被序列化),就可以為這些變數加上transient關鍵字(不能修飾方法和類)。換句話說,這個欄位的生命週期僅存於呼叫者的記憶體中而不會寫到磁盤裡持久化


2.3  檔案IPC示例

下面是一個簡單的示例,首先定義一個User類,該類必須實現Serializable或者是Parcelable介面,這樣便可以序列化。Serializable介面是Java提供的一個序列化介面,使用Serializable非常簡單,只要直接User implements Serializable即可,當然也可以在User類中設定serialVersionUID,如下所示。

private static final long serialVersionUID = 519067123721295773L;  

這個serialVersionUID會隨著序列化的行為被記錄下來,若不設定serialVersionUID,當反序列化時該類若發生變化,如增加或刪除了一些成員變數等等(修改型別等是不可以的),這樣在校驗serialVersionUID時沒有找到類中的宣告,系統會重新計算該值,那肯定就和以前記錄的不一樣啦,這樣就會反序列化失敗

Parcelable介面在開銷上較Serializable介面要小,更適合Android平臺Parcelable介面主要用於記憶體序列化上,當用於將物件持久化到儲存裝置或用於網路傳輸,使用Parcelable會很複雜,這種情況建議使用Serializable介面。

使用Parcelable介面的User類如下所示:

public class User implements Parcelable {  
    public int userId;  
    public String userName;  
  
    public User(int userId, String userName) {  
        this.userId = userId;  
        this.userName = userName;  
    }  
  
    public int describeContents() {  
        return 0;  
    }  
  
    public void writeToParcel(Parcel out, int flags) {  
        out.writeInt(userId);  
        out.writeString(userName);  
    }  
  
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {  
        public User createFromParcel(Parcel in) {  
            return new User(in);  
        }  
  
        public User[] newArray(int size) {  
            return new User[size];  
        }  
    };  
  
    private User(Parcel in) {  
        userId = in.readInt();  
        userName = in.readString();  
    }    
}  

剩下的就是在一個程序中將該物件序列化到檔案中的操作:

User user = new User(1, "SEU_Calvin");  
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(mFilePath));  
objectOutputStream.writeObject(user);  

最後在另一個程序中從檔案恢復之前儲存的User物件的內容,之所以說是內容,是因為反序列化得到的物件只是在內容上和序列化之前的物件是一樣的,但他們本質上是兩個物件

User user = null;  
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(mFilePath));  
user = (User) objectInputStream.readObject();  

本篇介紹了Android中常見的IPC方式中的Bundle和檔案,後面會陸續介紹其他的,如MessengerAIDLContentProviderSocket等方式。請大家多點贊支援~