Android的多進程通訊-深入了解
一、多進程是什么?
正常情況下,在Android系統中一個APP啟動后只會運行在一個進程中,其進程名為apk的包名,所有的組件都會在這個進程中運行;
但是如果需要將某些組件(如Service,Activity等)運行在單獨的進程中,就需要用到android:process屬性了。
我們可以給android的組件設置android:process屬性來使其運行在指定的進程中。
1、多進程優點:
①使用更多的內存
Android系統對每個應用進程的內存占用是有限制的,而且占用內存越大的進程,通常被系統殺死的可能性越大。讓一個組件運行在單獨的進程中,可以減少主進程所占用的內存,避免OOM問題,降低被系統殺死的概率。
②實現多模塊
比如微信的小程序(一個住進程,一個小程序進程)、支付寶的小程序;
當你啟動一個小程序時候,就會啟動小程序的進程,不占用主進程的內存,使小程序進程單獨出來,速度會快很多。
③子進程奔潰,主進程可以繼續工作
④主進程退出,子進程可以繼續工作
⑤實現守護進程
通過JNI利用C/C++,調用fork()方法來生成子進程,一般開發者會利用這種方法來做一些daemon(守護進程)進程,來實現防殺保活等效果。
2、多進程引起的問題
①靜態成員和單例模式失效
②線程同步機制失效
③SharedPreferences 可靠性降低
④Application 被多次創建
⑤多進程交互麻煩
引起的原因:
Android中,系統會為每個應用或進程分配獨立的虛擬機,不同的虛擬機自然占有不同的內存地址空間,
所以同一個類的對象會產生不同的副本,導致共享數據失敗,必然也不能實現線程的同步。
二、多進程通信方式
Android中支持的多進程通信方式主要有以下幾種,它們之間各有優缺點,可根據使用場景選擇選擇:
- AIDL:功能強大,支持進程間一對多的實時并發通信,并可實現 RPC (遠程過程調用)。
- Messenger:支持一對多的串行實時通信, AIDL 的簡化版本。
- Bundle:四大組件的進程通信方式,只能傳輸 Bundle 支持的數據類型。
- ContentProvider:強大的數據源訪問支持,主要支持 CRUD 操作,一對多的進程間數據共享,例如我們的應用訪問系統的通訊錄數據。
- BroadcastReceiver:即廣播
廣播是一種被動跨進程通訊的方式。當某個程序向系統發送廣播時,其他的應用程序只能被動地接收廣播數據。
這就象電臺進行廣播一樣,聽眾只能被動地收聽,而不能主動與電臺進行溝通,在應用程序中發送廣播比較簡單。
只需要調用sendBroadcast方法即可。該方法需要一個Intent對象。通過Intent對象可以發送需要廣播的數據
- 文件共享:在非高并發情況下共享簡單的數據。
- Socket:通過網絡傳輸數據。
這次介紹日常開發APP中常用的交互方式AIDL和Messenger
如圖
1、多進程AIDL通信
AIDL是Android接口定義語言是用于定義服務器和客戶端通信接口的一種描述語言,可以使用它定義客戶端與服務端進程間通信(IPC)的編程接口,
在 Android 中,進程之間無法共享內存(用戶空間),不同進程之間的通信一般使用 AIDL 來處理。
使用簡單步驟:
創建一個AIDL文件(擴展名為.aidl);
暴露一個接口給客戶端(通過建立一個Service,在onBind()方法中返回一個Stub類的實例);
服務端實現該AIDL文件生成的Java接口(系統會自動生成對應的Java接口);
客戶端連接綁定該遠程服務。
①創建AIDL文件
創建一個 AIDL 文件,聲明服務端要暴露給客戶端的接口,然后創建一個 Service 監聽客戶端的連接請求,并在其中實現 AIDL 文件中的接口
注意,為了方便開發,我們一般把 AIDL 相關的文件放在同一包中,這樣當客戶端是另一個應用時可方便的把整個包復制到客戶端工程中
首先了解下 AIDL 文件支持的幾種數據類型:
- 基本數據類型
- String、CharSequence
- ArrayList、HashMap,其內部元素也需要被AIDL支持
- 實現了 Parcelable 接口的對象
- AIDL 類型的接口,非普通接口
Book是實現了Parcelable的類,只定義了name字段,按照規定如果 AIDL 文件用到了自定義Parcelable對象,同時需要提供一個Book.aidl文件
- package com.json;
- parcelable Book;
②ILibraryManager.aidl定義了服務端要暴露給客戶端的接口:
- package com.json;
- import com.json.Book;
- interface ILibraryManager{
- // 近期新書查詢
- List<Book> getNewBookList();
- // 圖書捐贈
- void donateBook(in Book book);
- }
③接下來就是AIDL文件生成的Java接口,在編寫服務類前要先編譯項目,這樣在服務類里使用 AIDL 生成的java類
首先通過ILibraryManager.Stub()創建一個mBinder對象,并實現了ILibraryManager.aidl中定義的接口方法,在onBind()方法中返回創建的mBinder,并在服務onCreate()添加數據。
最后在 AndroidManifest.xml 注冊服務:
- <service
- android:name=".ManagerService"
- android:process=":remote">
- </service>
- public class ManagerService extends Service {
- private static final String TAG = "ManagerService";
- // CopyOnWriteArrayList 支持并發讀寫
- private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
- private Binder mBinder = new ILibraryManager.Stub() {
- @Override
- public List<Book> getNewBookList() throws RemoteException {
- return mBookList;
- }
- @Override
- public void donateBook(Book book) throws RemoteException {
- mBookList.add(book);
- }
- };
- public LibraryManagerService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- mBookList.add(new Book("book0"));
- mBookList.add(new Book("book1"));
- }
- }
④客戶端連接綁定該遠程服務
先實現ServiceConnection接口,在onServiceConnected()方法中將IBinder對象轉換成ILibraryManager對象,通過該對象就能調用ILibraryManager.aidl中聲明的方法了。
接下來綁定服務:
- public class AIDLActivity extends AppCompatActivity {
- private static final String TAG = "AIDLActivity";
- ILibraryManager libraryManager=null;
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- libraryManager = ILibraryManager.Stub.asInterface(service);
- try {
- // test1
- List<Book> books = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books.toString());
- // test2
- libraryManager.donateBook(new Book("book" + books.size()));
- List<Book> books2 = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books2.toString());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
- void bindAidlData(){
- if(null==libraryManager){
- return;
- }
- try {
- // test1
- List<Book> books = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books.toString());
- // test2
- libraryManager.donateBook(new Book("book" + books.size()));
- List<Book> books2 = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books2.toString());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_aidl);
- bindNewService();
- }
- private void bindNewService() {
- Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
- bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- super.onDestroy();
- }
- }
2、aidl的通知功能
首先創建一個服務端通知客戶端的 IOnNewBookArrivedListener.aidl 接口:
- package com.json;
- import com.json.Book;
- interface IOnNewBookArrivedListener {
- void onNewBookArrived(in Book book);
- }
服務端要先注冊后才能收到通知,同時也可以取消注冊,所以要給之前的ILibraryManager.aidl添加連個方法了:
- interface ILibraryManager{
- ......
- // 注冊通知
- void register(IOnNewBookArrivedListener listener);
- // 取消注冊
- void unregister(IOnNewBookArrivedListener listener);
- }
修改ManagerService:
- public class ManagerService extends Service {
- ......
- ......
- ......
- // 系統提供的專門用于保存、刪除跨進程 listener 的類
- private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
- // AtomicBoolean 支持并發讀寫
- private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
- private Binder mBinder = new ILibraryManager.Stub() {
- ......
- @Override
- public void register(IOnNewBookArrivedListener listener) throws RemoteException {
- mListenerList.register(listener);
- Log.e(TAG, "register success");
- }
- @Override
- public void unregister(IOnNewBookArrivedListener listener) throws RemoteException {
- mListenerList.unregister(listener);
- Log.e(TAG, "unregister success");
- }
- };
- .......
- @Override
- public void onCreate() {
- super.onCreate();
- ......
- new Thread(new Runnable() {
- @Override
- public void run() {
- // 如果服務還沒終止
- while (!mIsServiceDestroy.get()) {
- try {
- Thread.sleep(10 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Book book = new Book("book" + mBookList.size());
- mBookList.add(book);
- bookArrivedNotify(book);
- }
- }
- }).start();
- }
- private void bookArrivedNotify(Book book) {
- int n = mListenerList.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
- if (listener != null) {
- try {
- listener.onNewBookArrived(book);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
- mListenerList.finishBroadcast();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- mIsServiceDestroy.set(true);
- }
- }
這里用到了RemoteCallbackList類,它是系統提供的專門用于刪除跨進程 listener 的類,用普通的集合難以保證客戶端注冊的 listener 和服務端存儲的 listener 是同一個,會取消注冊失敗
客戶端代碼修改
- public class AIDLActivity extends AppCompatActivity {
- ......
- private ILibraryManager mLibraryManager;
- private Handler mHandler = new Handler(new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_NEW_BOOK_ARRIVED:
- Log.e(TAG, "new book:" + msg.obj);
- break;
- }
- return true;
- }
- });
- private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
- @Override
- public void onNewBookArrived(Book book) throws RemoteException {
- // 由于 onNewBookArrived 方法在子線程被調用,所以通過Handler切換到UI線程,方便UI操作
- mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
- }
- };
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
- mLibraryManager = libraryManager;
- try {
- ......
- // 注冊通知
- libraryManager.register(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- ......
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_aidl);
- bindNewService();
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) {
- try {
- // 取消注冊
- mLibraryManager.unregister(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- super.onDestroy();
- }
- }
3、Messenger通訊
Messenger 是一種輕量級的多進程通信方式,它是在 AIDL 的基礎上封裝而成的,可以看做是 AIDL 的簡化版,支持一對多的串行實時通信,
一次只處理一個請求,不存在并發的問題。和 AIDL 的使用類似,但要簡單的多,同樣需要實現服務端和客戶端。
首先來看服務端
- public class MessengerService extends Service {
- private static final String TAG = "MessengerService";
- // 將Messenger和Handler關聯起來
- private Messenger mServiceMessenger = new Messenger(new MessengerHandler());
- public MessengerService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mServiceMessenger.getBinder();
- }
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessengerActivity.MESSAGE_FROM_CLIENT:
- // 打印接收到的客戶端消息
- Log.e(TAG, "receive message from client:" + msg.getData().getString("msg"));
- // 給客戶端回復一條消息
- Messenger clientMessenger = msg.replyTo;
- Message message = Message.obtain();
- message.what = MessengerActivity.MESSAGE_FROM_SERVICE;
- Bundle bundle = new Bundle();
- bundle.putString("msg", "I am fine,thank you!");
- message.setData(bundle);
- try {
- clientMessenger.send(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- break;
- }
- }
- }
- }
看客戶端的實現
- public class MessengerActivity extends AppCompatActivity {
- private static final String TAG = "MessengerActivity";
- public static final int MESSAGE_FROM_CLIENT = 1;
- public static final int MESSAGE_FROM_SERVICE = 2;
- private Messenger mServiceMessenger;
- private Messenger mClientMessenger = new Messenger(new MessengerHandler());
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mServiceMessenger = new Messenger(service);
- Message message = Message.obtain();
- message.what = MESSAGE_FROM_CLIENT;
- Bundle bundle = new Bundle();
- bundle.putString("msg", "how are you?");
- message.setData(bundle);
- // 傳遞服務端回復客戶端時需要使用的Messenger
- message.replyTo = mClientMessenger;
- try {
- mServiceMessenger.send(message);
- } 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(MessengerActivity.this, MessengerService.class);
- bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- super.onDestroy();
- }
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessengerActivity.MESSAGE_FROM_SERVICE:
- Log.e(TAG, "receive message from service:" + msg.getData().getString("msg"));
- break;
- }
- }
- }
- }
在onServiceConnected()中,將服務端的Binder轉換成服務端的Messenger對象,然后發送消息,由于服務端還需要給客服端回復消息,所以需要在客戶端創建一個Messenger對象附加在消息上發送給服務端使用
多進程總結:
1、多進程app可以在系統中申請多份內存,但應合理使用,建議把一些高消耗但不常用的模塊放到獨立的進程,不使用的進程可及時手動關閉;
2、多進程占用內存多,耗電量會增加,這個要注意;
3、每個進程在創建的時候,都會執行Application的onCreate進行初始化,如果這時候沒有針對不同進程處理,onCreate的初始化業務會被多次執行,這是沒有必要的而且多次初始化容易引起問題,所以需根據不同進程初始化相應的業務。
本文轉載自微信公眾號「 Android開發編程」,可以通過以下二維碼關注。轉載本文請聯系 Android開發編程公眾號。