IPC是Inter-ProcessCommunication的縮寫,進程間通信、跨進程通信,是指兩個進程之間進行數據交換的過程。說起進程間通信,我們首先腦補下什么是進程,什么是線程,進程和線程是截然不同的概念。按照操作系統中的描述,線程是CPU調度的最小單元,同時線程是一種有限的系統資源。而進程一般指一個執行單元,在PC和移動設備上指一個程序或者一個應用。一個進程可以包含多個線程,因此進程和線程是包含與被包含的關系。最簡單的情況下,一個進程中可以只有一個線程,即主線程,在Android里面主線程也叫UI線程,在UI線程里才能操作界面元素。通常情況下一個進程中需要執行大量耗時的任務,如果這些任務放在主線程中去執行就會造成界面長時間無法響應,嚴重影響用戶體驗,這種情況在PC系統和移動系統中都存在,在Android中有一個特殊的名字叫做ANR(ApplicationNotResponding),即應用無響應。解決這個問題就需要用到線程,把一些耗時的任務放在線程中即可。
對于Android來說,它是一種基于Linux內核的移動操作系統,它的進程間通信方式并不能完全繼承自Linux,它有自己的進程間通信方式。在Android中最有特色的進程間通信方式就是Binder了,通過Binder可以輕松地實現進程間通信。除了Binder,Android還支持Socket,通過Socket也可以實現任意兩個終端之間的通信,當然同一個設備上的兩個進程通過Socket通信自然也是可以的。
具體IPC方式有很多,比如可以通過在Intent中附加extras來傳遞信息,或者通過共享文件的方式來共享數據,還可以采用Binder方式來跨進程通信,另外,ContentProvider天生就是支持跨進程訪問的,因此我們也可以采用它來進行IPC。此外,通過網絡通信也是可以實現數據傳遞的,所以Socket也可以實現IPC。下面就具體介紹幾種主要的IPC方式。
1.使用Bundle
四大組件中的三大組件(Activity、Service、BroadcastReceiver)都是支持在Intent中傳遞Bundle數據的,由于Bundle實現了Parcelable接口,所以它可以方便地在不同的進程間傳輸。這里傳輸的數據必須可以被序列化,比如基本數據類型和實現了Parcelable、Serializable接口的類都可以傳輸。
2.使用文件共享
這個看起來比較好理解,就是在SD卡中存儲個文件用來共享,但是這個明顯的有并發問題。眾所周知,SharedPreferences是Android提供的一個輕量級的存儲方案,在xml中以鍵值對的形式存儲。同樣的,在多進程的情況下,讀寫并不是可靠的。
3.使用Messenger
Messenger很多地方翻譯為信使,這個也算貼切。Messenger是一中輕量級的IPC方式,底層實現也是基于AIDL。
Messenger的使用方法很簡單,它對AIDL做了封裝,使得我們可以更簡便地進行進程間通信。同時,由于它一次處理一個請求,因此在服務端我們不用考慮線程同步的問題,這是因為服務端中不存在并發執行的情形。實現一個Messenger有如下幾個步驟,分為服務端和客戶端。
A.服務端進程首先,我們需要在服務端創建一個Service來處理客戶端的連接請求,同時創建一個Handler并通過它來創建一個Messenger對象,然后在Service的onBind中返回這個Messenger對象底層的Binder即可。
B.客戶端進程客戶端進程中,首先要綁定服務端的Service,綁定成功后用服務端返回的IBinder對象創建一個Messenger,通過這個Messenger就可以向服務端發送消息了,發消息類型為Message對象。如果需要服務端能夠回應客戶端,就和服務端一樣,我們還需要創建一個Handler并創建一個新的Messenger,并把這個Messenger對象通過Message的replyTo參數傳遞給服務端,服務端通過這個replyTo參數就可以回應客戶端。這聽起來可能還是有點抽象,不過看了下面的兩個例子,讀者肯定就都明白了。首先,我們來看一個簡單點的例子,在這個例子中服務端無法回應客戶端。
/**
* Created by Stone on 2018/8/16.
*/
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler
{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = " MessengerActivity";
private TextView tvResult;
private Messenger mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null, 1);
Bundle data = new Bundle();
data.putString("msg", " hello, this is client.");
msg.setData(data);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
tvResult = findViewById(R.id.tv_result);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
問題來了,實際使用中不可能只有客戶端訪問服務端,還有服務端響應客戶端,那我們怎么改進下呢?直接上代碼:
/**
* Created by Stone on 2018/8/16.
*/
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler
{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString(" reply", "消息已收到");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = " MessengerActivity";
private TextView tvResult;
private Messenger mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null, 1);
Bundle data = new Bundle();
data.putString("msg", " hello, this is client.");
msg.setData(data);
//添加處理回復的Messenger
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
Log.i(TAG, " receive msg from Service:" + msg.getData().getString(" reply"));
tvResult.append("\n receive msg from Service:" + msg.getData().getString(" reply"));
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
tvResult = findViewById(R.id.tv_result);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
從上面代碼可以看到,在收到客戶端的消息后,服務端把返回的消息放到了msg.replyTo中,這也是一個Messenger,具體接受的Messenger在MessengerActivity中定義,當然也是一組完整的Messenger跟Handler,具體可以看代碼。這里IPC的類型必須是Message支持的類型。最后給出Messenger的工作原理:
4.使用AIDL
簡單的使用可參考Android Binder---AIDL ,這里接著Android Binder---AIDL介紹的AIDL繼續往下寫??紤]到用戶每次都是訪問getStudent方法比較費勁,如果StudentList有更新再通知用戶去獲取豈不是很省力?這里就要用到觀察者模式了。我們創建一個新的AIDL接口,這個用來監聽StudentList的變化。
// IOnNewStudentArrivedListener.aidl
package com.stone.demoandroid.entity;
import com.stone.demoandroid.entity.Student;
// Declare any non-default types here with import statements
interface IOnNewStudentArrivedListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onNewStudentArrived(in Student newStudent);
}
同樣的,之前的Manager也要有所變化添加兩個方法:registerListener、unregisterListener
// IStudentManager.aidl
package com.stone.demoandroid.entity;
// Declare any non-default types here with import statements
import com.stone.demoandroid.entity.Student;
import com.stone.demoandroid.entity.IOnNewStudentArrivedListener;
interface IStudentManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
List<Student> getStudentList();
void addStudent(in Student student);
void registerListener( IOnNewStudentArrivedListener listener);
void unregisterListener( IOnNewStudentArrivedListener listener);
}
Service跟Activity的代碼太長了就不全部貼出來了,有興趣的同學可以直接去Demo下載查看。這里有一點需要提一下,CopyOnWriteArrayList不能處理跨進程的Listener,需要用RemoteCallbackList,因為CopyOnWriteArrayList在服務端使用的Listener并不是客戶端定義的那個,看名字也知道只是一個Copy。還有就是并沒有處理ANR和Binder意外死亡的問題,有興趣額同學可以處理下。
5.使用ContentProvider
ContentProvider是Android中提供的專門用于不同應用間進行數據共享的方式,從這一點來看,它天生就適合進程間通信。和Messenger一樣,ContentProvider的底層實現同樣也是Binder,由此可見,Binder在Android系統中是何等的重要。雖然ContentProvider的底層實現是Binder,但是它的使用過程要比AIDL簡單許多,這是因為系統已經為我們做了封裝,使得我們無須關心底層細節即可輕松實現IPC。具體的實現這里就不贅述了,四大組件之一,用法一搜一堆,有興趣的可以看下Demo
6.使用Socket
Socket也稱為“套接字”,是網絡通信中的概念,它分為流式套接字和用戶數據報套接字兩種,分別對應于網絡的傳輸控制層中的TCP和UDP協議。本科的時候選修過JavaSocket,作業有實現TCP和UDP協議,考試竟然是上機,自己實現一個局域網的聊天工具。
具體的實現,大家可以看下Demo
選擇合適的IPC(任玉剛. Android開發藝術探索. 電子工業出版社. ),這里借大佬總結的帖在這里希望對大家有幫助:
主要的幾個IPC的方式就簡單介紹到這里,有什么問題可以留言。