IPC簡介以及基本知識
1 IPC含義
程式間通訊或者跨程式通訊,是指兩個程式之間進行資料交換的過程。程式指的一般都為一個程式或者一個應用,一個程式可以包含多個執行緒,執行緒指的是一個執行單元(如UI執行緒等等)
2 IPC基本知識
要理解IPC機制,學習IPC的一些基本知識概念是必須的,主要包含三個內容:物件序列化,Binder。
2.1 序列化
2.1.1 Serializable序列化
類直接實現Serializable介面,VersionUID可寫可不寫,寫時有利於反序列化的進行,不寫時有時候會造成物件反序列化失敗。失敗的原因:序列化時會判斷要反序列化的物件是否還是和當前類是同一個類,若不是則反序列化失敗(增加資料,刪除資料等時類會改變)
2.1.2 Parcelable
實現Parcelable介面,對一個完整的物件進行分解,而分解後的每一個部分都是Intent所支援的資料型別
2.2 Binder
2.2.1 Binder是Android中一種跨程式通訊方式
- 從Android Framework來看,Binder是ServiceManager連線各種Manager(AcitvityManager,WindowManager等)和相應ServiceManager的橋樑。
- 從Android應用層來說,Binder是客戶端與服務端進行通訊的媒介(當客戶端bindService時,服務端就會返回一個Binder物件,通過對這個物件的操作,客戶端就可以獲取到服務端的資料等)
2.2.2 Binder底層實現
3 Android中的IPC方式
3.1 使用Bundle
Activity,Service,Receiver三大元件都支援在Intent中傳遞Bundle資料
往一個服務端中傳送資料:
Intent intent = new Intent(MainActivity.this,MyDownloadService.class);
Bundle bundle = new Bundle();
bundle.putString("key","value");
intent.putExtras(bundle);
複製程式碼
在服務端中接收:
Bundle mBundle = getIntent().getExtras();
if(mBundle!=null) {
String data = mBundle.getString("key");
}
複製程式碼
3.2 檔案共享
在一個程式中寫入(ObjectOutputStream)資料到檔案,在另一個程式中讀取(ObjectInputStream)檔案資料,從而也可實現多程式資料交流
特別:SharedPreferences,它通過鍵值對儲存資料,在底層採用XML檔案儲存鍵值對,但於系統對它的讀寫有一定的快取策略,,即記憶體中會有一份SharedPreferences檔案的快取,因此多程式模式下,系統對它的讀寫變得不可靠,容易丟失資料,不建議在多程式中採用
3.3 使用Messenger(信使)
通過Messenger可以在不同程式間傳遞Message物件,底層實現是AIDL,Messenger一次只處理一個請求,所以在客戶端我們不用考慮執行緒同步問題,因為它不存在併發的問題
3.3.1 服務端程式
- 建立Service來響應客戶端的請求,同時建立一個Handler,並從handleMessage()方法中獲取客戶端傳來的Messenger物件,通過這個Messenger傳送訊息message給客戶端從而實現服務端向客戶端發訊息,並通過此Handler來建立一個服務端的Messenger物件,
/**
* 處理客戶端發來的資訊
*/
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d("chen", "從客戶端接收到資訊:" + msg.getData().getString("msg"));
//獲取客戶端的Messenger
Messenger client = msg.replyTo;
//建立一條訊息
Message replyMessage = Message.obtain(null,2);
Bundle bundle = new Bundle();
bundle.putString("reply","服務端已經收到資訊");
//往客戶端中傳送訊息
replyMessage.setData(bundle);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
//通過Handler建立一個Messenger物件
private final Messenger messenger = new Messenger(new MessengerHandler());
複製程式碼
- 然後在Service的onBind中返回這個服務端的Messenger物件底層的Binder給客戶端進行操作
@Nullable
@Override
public IBinder onBind(Intent intent) {
//給客戶端返回Binder,以便可以進行程式間的資料交流
return messenger.getBinder();
}
複製程式碼
3.3.2 客戶端程式
- 首先要建立一個ServiceConnection來繫結服務端的Service,繫結成功後用伺服器返回後的IBinder物件來建立一個Messenger物件
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//1獲取Messenger物件
Messenger messenger = new Messenger(service);
......
}
複製程式碼
- 通過這個物件就可以向伺服器傳送訊息了,傳送訊息的型別為Message物件,並建立一個ReplyMessenger傳給服務端,且傳送的訊息是到服務端中建立Messenger時所傳入的Handler中的handlerMessage()方法中獲取資訊
//2建立一個Message物件,引數一:Handler 引數二:Message的傳送碼
Message message = Message.obtain(null, 1);
//3建立一個包裹進行資料的存放
Bundle data = new Bundle();
data.putString("msg", "這裡是客戶端");
message.setData(data);
//5返回一個接收伺服器資訊的Messenger物件給伺服器,讓伺服器進行傳送訊息,從而在客戶端中可以接收
message.replyTo = getReplyMessenger;
try {
//開始傳送訊息
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
複製程式碼
- 如果需要服務端迴應客戶端,就和服務端一樣,我們還需要建立一個Handler並建立一個新的Messenger,並把這個Messenger物件通過客戶端傳遞給服務端的Messange的replyTo引數傳遞給服務端,服務端通過獲取這個replyTo引數就可以獲取到客戶端的Messenger物件,從而實現向客戶端進行傳送訊息
/**
* 獲取伺服器返回訊息的Messenger
*/
private Messenger getReplyMessenger = new Messenger(new getMessengerHandler());
private static class getMessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
Log.d("chen", "從伺服器接收到訊息" + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
複製程式碼