一、基礎知識
1.一些概念
IPC : Inter-process Communication 跨程序通信,是指兩個程序之間進行資料交換的過程。
線程:線程是 CPU 排程的最小單元,同時線程是一種有限的系統資源。
程序:一般是指一個執行單元, 在 PC 和移動裝置上指一個程式或者一個應用。 程序可以包含多個線程。
2. Android 中的多程序模式
(1).開啟多程序模式
給四大元件在 AndroidManifest 中指定 android:process 屬性,即可開啟。
通過指令行 adb shell ps 可以看見程序資訊。
(2).多程序模式的運作機制
系統會為每一個程序配置設定一個獨立的虛拟機,不同的虛拟機在記憶體配置設定不同的位址空間。
使用多程序會造成的問題:
.靜态成員和單例模式完成失效;
.線程同步機制完成失效;
.SharedPreferences 的可靠性下降;
.Application 會多次建立(因為運作同一個程序中的元件是屬于同一個虛拟機和同一個 Application 的)
二、IPC 基礎概念介紹
1、Serializable 接口
Serializable 是 Java 提供的一個接口,是一個空接口,為對象提供标準的序列化和反序列化操作;
serialVersionUID : 用來輔助序列化和反序列化過程的,原則上序列化後的資料中的 serialVersionUID 隻有和目前類的 serialVersionUID 相同 才能正常地被反序列化。 一般手動指定 serialVersionUID 的值,是因為反序列化時目前類有所改變時,系統會重新計算目前類的 hash 值,并把它指派 給 serialVersionUID ,這個時候目前類的 serialVersionUID 和反序列化的資料中的 serailVersionUID 不一緻,會出現反序列化 失敗,程式會出現 crash。
注意事項: .靜态成員變量屬于類而不屬于對象,是以不會參與序列化過程; .用 transient 關鍵字的成員變量不參與序列化過程。
2、Parcelable 接口
Interface for classes whose instance can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of the type implements Parcelable.Creator interface.
Serializable 與 Parcelable 的差別與選用
Serializable 開銷大,序列化和反序列化過程需要大量的 I/O 操作,如果是存儲在裝置中,網絡傳輸,一般使用 Serializable;
Parcelable 主要用在記憶體序列化上,是 Android 中的方法。
3、Binder
Binder 是 Android 中的一個類,它實作 IBinder 接口。從 IPC 的角度來說, Binder 是 Android 中的一種跨程序通訊方式。 Android 開發中, Binder 主要用在 Service 中,包括 AIDL 和 Messenger .
三、 Android 中的 IPC 方式
1、使用 Bundle
2、使用檔案共享
序列化一個對象到系統檔案中,同時從另一個程序中恢複這個對象。
發序列化得到的對象隻是在内容上和序列化之前的對象是一樣的,但它們本質上還是兩個對象。
使用檔案方式來共享資料對檔案格式沒有具體的要求,隻要讀寫雙方約定資料格式即可。但是這種方式會面臨并發讀寫的局限性。
檔案共享方式适合在對資料同步要求不高的程序之間進行通信,并且要處理并發讀寫的問題。
系統對 SharedPreference 的讀寫有一定的緩存,在多程序中,SP 面對高并發的讀寫通路會丢失資料,是以,盡量不要在程序間通信中使用 SP.
3、使用 Messenger
輕量級 IPC 方案,底層實作是 AIDL ,串行處理消息。 例子 服務端程序
public class MessengerService extends Service {
private static final String TAG = "message";
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case Constants.MSG_FROM_CLIENT:
Log.i(TAG, "receive msg from Client : " + msg.getData().getString("msg"));
// 回複客服端
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "你的消息已收到");
replyMessage.setData(bundle);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
用戶端
public class MessengerActivity extends Activity {
private static final String TAG = "message";
private Messenger mMessenger;
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
Message msg = Message.obtain(null, Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "Hello, this is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case Constants.MSG_FROM_SERVICE:
Log.i(TAG, "receive msg from Service : " + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
break;
}
}
}
}
在 Messenger 中進行資料傳遞必須将資料放入到 Message 中,而 Messenger 和 Message 都實作了 Parcelable 接口,是以可以跨程序傳輸。
4、使用 AIDL
使用 AIDL 比較複雜,這裡掠過
5、使用 Socket
使用 Socket 通信,首先要聲明權限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
其次是不能在主線程中通路網絡。
四、選用合适的 IPC 方式
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyMwEjN1UDM2EjMwUDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)