Android 中進行 IPC 的方式有很多枝恋,比如 在 Activity 或者廣播中用 Intent 傳遞小數據段标、共享文件沃但、Binder拐辽、ContentProvider拣挪、Socket。下面詳細介紹一下各種方式俱诸。
1.使用 Bundle
Android 的 Activity 菠劝、Service 、Recriver 支持在 Intent 中傳遞 Bundle 數據睁搭,由于 Bundle 實現了 Parcelable 接口赶诊,所以當我們在一個進程啟動另一個進程的 Activity 、Service 园骆、Receiver 后舔痪,我們可以在 Bundle 中附加我們需要傳遞的數據,其中包括基本數據類型锌唾、實現了 Parcelable 或者 Serializable 接口的對象還有一些 Android 支持的特殊對象锄码。
2.使用文件共享
兩個進程通過讀/寫同一個文件來交換數據也可以進行 IPC 。但是注意從文件中反序列化得到的對象和原來序列化的對象不是同一個對象,他們只是內容相同而已巍耗。這種方式適合在對數據同步要求不高的進程之間進行通信秋麸,并且要妥善處理并發(fā)的讀/寫問題。
PS: 使用 SharePrefrences 在面對高并發(fā)的讀/寫訪問的情況下炬太,會有很大幾率會丟失數據灸蟆,因此 IPC 不建議使用 SharePrefrences 。
3.使用 Messenger
Messenger 其實是封裝好的 AIDL 亲族,而且由于它一次只處理一個請求炒考,因此在服務端不需要考慮線程同步的問題。
下面我們來使用一下 Messenger 霎迫。
-
1)建立服務端進程
我們在服務端創(chuàng)建一個 Service 來處理客戶端的連接請求锭部,并且通過創(chuàng)建一個 Handler 來構建服務端的 Messenger 對象妒貌,然后在 Service 的
onBind()
中返回服務端 Messenger 的 Binder 對象,可以理解成:我(服務端)將可以和我通信的工具(Binder)經過我的 Messenger 包裝后,發(fā)給你(客戶端)觉痛,你通過這個工具包裝成一個 Messenger 對象才可以發(fā)消息給我躺酒。如果服務端想要回復客戶端消息啸箫,就要客戶端在發(fā)送過來的 Message 中包含一個用于回復客戶端的 Messenger 對象馆里,服務端收到這個對象后用這個對象發(fā)送消息返回給客戶端。
服務端的源碼:https://github.com/innovatorCL/IPCServer
show my code :
/** * * 使用 Messenger 方式的服務端 * Created by innovator on 2018/1/24. */ public class MessengerService extends Service{ private static final String TAG = "TAG"; private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what){ case MyConstants.MSG_FROM_CLIENT: Log.i(TAG,"服務端收到客戶端發(fā)來的消息:"+msg.getData().getString("msg")); //接收 客戶端 傳過來的 Messenger筒扒,用來回復 客戶端 Messenger clientMessenger = msg.replyTo; Message message = Message.obtain(null,MyConstants.MSG_REPLY_TO_CLIENT); Bundle data = new Bundle(); data.putString("msg","你好怯邪,我是服務器,很高興能和你對話"); message.setData(data); try{ clientMessenger.send(message); }catch (RemoteException e){ Log.i(TAG,"服務端回應客戶端異常:"+e.getMessage()); } break; default: break; } super.handleMessage(msg); } } //構造服務器的 Messenger private final Messenger messenger = new Messenger(new MessengerHandler()); //連接客戶端的時候返回服務器的Binder對象給客戶端 @Nullable @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } }
-
2)建立客戶端進程
我們在客戶端中首先要綁定服務端的 Service花墩,綁定成功后悬秉,通過服務端返回的 Binder 對象構造客戶端的 Messenger, 這樣就可以通過這個 Messenger 來給服務端發(fā)送消息了冰蘑,發(fā)送的消息類型是 Message 對象和泌。可以看到,用 Messenger 方式進行 IPC 的靈魂人物是 Binder懂缕,這個其實就是 Messenger 和 AIDL 底層的東西允跑。
上面只是客戶端發(fā)送消息給服務端需要做的事王凑,如果服務端需要回應客戶端搪柑,就要在客戶端創(chuàng)建一個新的 Handler,然后通過這個 Handler 來創(chuàng)建一個新的 Messenger對象索烹,然后將這個新的 Messenger 對象放在上面客戶端給服務端發(fā)送的 Message 里面(將這個新的 Messenger 作為 Message 的
replyTo()
方法的參數就可以了)工碾。 當服務端拿到這個客戶端傳過來的 Messenger 對象,就可以給客戶端發(fā)送消息了百姓。
show my code
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
//將從服務器獲取到的 Binder 對象包裝成自己的 Messenger 對象渊额,從而給服務端發(fā)送消息
private Messenger mMessenger = null;
private static final int SAY_HELLO = 0;
private boolean mBound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("TAG","通過 Messenger 連接服務器成功");
mBound = true;
//根據服務器返回的 Binder 對象創(chuàng)建 Messenger 對象,并使用這個對象向服務器發(fā)送數據
mMessenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("TAG","斷開與服務器的連接");
mBound = false;
}
};
//構造用于服務器回復客戶端的Handler
private static class ReplyMessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_REPLY_TO_CLIENT:
Log.i("TAG","客戶端接收到了服務端通過 Messenger 發(fā)送過來的消息:"+msg.getData().getString("msg"));
break;
default:
super.handleMessage(msg);
break;
}
}
};
//構造用于服務器回復客戶端的 Messeenger
private Messenger mReplyMessenger = new Messenger(new ReplyMessengerHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
attempToBind();
}
@Override
protected void onStart() {
super.onStart();
attempToBind();
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
private void attempToBind(){
if(!mBound){
Intent i = new Intent();
i.setAction("com.innovator.messenger");
i.setPackage("com.innovator.ipcserver");
bindService(i,connection, Context.BIND_AUTO_CREATE);
}
}
public void sayHello(View v){
if(mMessenger == null){
attempToBind();
return;
}
if(mMessenger != null){
Message message = Message.obtain(null,SAY_HELLO);
Bundle data = new Bundle();
data.putString("msg","你好,這是在客戶端發(fā)送給你的消息");
message.setData(data);
//將可以回復自身(客戶端)的工具 mReplyMessenger 傳給 服務端
message.replyTo = mReplyMessenger;
try {
mMessenger.send(message);
}catch (RemoteException e){
Log.i("TAG","客戶端發(fā)送信息異常:"+e.getMessage());
}
}
}
}
- 調試的結果:
1.當客戶端連接到服務端的 Service 旬迹,然后給服務端發(fā)送消息時火惊,服務端打印的 Log :
2.客戶端打印的 Log :
- 總結一下
Messenger 方式是以串行的方式處理客戶端發(fā)送的消息的,如果有大量消息同時發(fā)送到服務端奔垦,服務端仍然只能一個一個處理屹耐,所以當有大量的并發(fā)請求,使用 Messenger 就不太合適了椿猎。同時惶岭,Messenger 只能用來傳遞消息,不能跨進程調用服務端的方法犯眠,這種情況也不適合使用 Messenger按灶。
4.使用 ContentProvider
本次講的內容已經比較多了,相信看到這里的同學應該已經疲勞了筐咧,ContentProvider 和 Socket 的內容比較多鸯旁,我們放在下一節(jié)來看吧。