Android的多進程通訊-深入了解
一、多進程是什么?
正常情況下,在Android系統(tǒng)中一個APP啟動后只會運行在一個進程中,其進程名為apk的包名,所有的組件都會在這個進程中運行;
但是如果需要將某些組件(如Service,Activity等)運行在單獨的進程中,就需要用到android:process屬性了。
我們可以給android的組件設(shè)置android:process屬性來使其運行在指定的進程中。
1、多進程優(yōu)點:
①使用更多的內(nèi)存
Android系統(tǒng)對每個應用進程的內(nèi)存占用是有限制的,而且占用內(nèi)存越大的進程,通常被系統(tǒng)殺死的可能性越大。讓一個組件運行在單獨的進程中,可以減少主進程所占用的內(nèi)存,避免OOM問題,降低被系統(tǒng)殺死的概率。
②實現(xiàn)多模塊
比如微信的小程序(一個住進程,一個小程序進程)、支付寶的小程序;
當你啟動一個小程序時候,就會啟動小程序的進程,不占用主進程的內(nèi)存,使小程序進程單獨出來,速度會快很多。
③子進程奔潰,主進程可以繼續(xù)工作
④主進程退出,子進程可以繼續(xù)工作
⑤實現(xiàn)守護進程
通過JNI利用C/C++,調(diào)用fork()方法來生成子進程,一般開發(fā)者會利用這種方法來做一些daemon(守護進程)進程,來實現(xiàn)防殺?;畹刃Ч?/p>
2、多進程引起的問題
①靜態(tài)成員和單例模式失效
②線程同步機制失效
③SharedPreferences 可靠性降低
④Application 被多次創(chuàng)建
⑤多進程交互麻煩
引起的原因:
Android中,系統(tǒng)會為每個應用或進程分配獨立的虛擬機,不同的虛擬機自然占有不同的內(nèi)存地址空間,
所以同一個類的對象會產(chǎn)生不同的副本,導致共享數(shù)據(jù)失敗,必然也不能實現(xiàn)線程的同步。
二、多進程通信方式
Android中支持的多進程通信方式主要有以下幾種,它們之間各有優(yōu)缺點,可根據(jù)使用場景選擇選擇:
- AIDL:功能強大,支持進程間一對多的實時并發(fā)通信,并可實現(xiàn) RPC (遠程過程調(diào)用)。
- Messenger:支持一對多的串行實時通信, AIDL 的簡化版本。
- Bundle:四大組件的進程通信方式,只能傳輸 Bundle 支持的數(shù)據(jù)類型。
- ContentProvider:強大的數(shù)據(jù)源訪問支持,主要支持 CRUD 操作,一對多的進程間數(shù)據(jù)共享,例如我們的應用訪問系統(tǒng)的通訊錄數(shù)據(jù)。
- BroadcastReceiver:即廣播
廣播是一種被動跨進程通訊的方式。當某個程序向系統(tǒng)發(fā)送廣播時,其他的應用程序只能被動地接收廣播數(shù)據(jù)。
這就象電臺進行廣播一樣,聽眾只能被動地收聽,而不能主動與電臺進行溝通,在應用程序中發(fā)送廣播比較簡單。
只需要調(diào)用sendBroadcast方法即可。該方法需要一個Intent對象。通過Intent對象可以發(fā)送需要廣播的數(shù)據(jù)
- 文件共享:在非高并發(fā)情況下共享簡單的數(shù)據(jù)。
- Socket:通過網(wǎng)絡(luò)傳輸數(shù)據(jù)。
這次介紹日常開發(fā)APP中常用的交互方式AIDL和Messenger
如圖
1、多進程AIDL通信
AIDL是Android接口定義語言是用于定義服務(wù)器和客戶端通信接口的一種描述語言,可以使用它定義客戶端與服務(wù)端進程間通信(IPC)的編程接口,
在 Android 中,進程之間無法共享內(nèi)存(用戶空間),不同進程之間的通信一般使用 AIDL 來處理。
使用簡單步驟:
創(chuàng)建一個AIDL文件(擴展名為.aidl);
暴露一個接口給客戶端(通過建立一個Service,在onBind()方法中返回一個Stub類的實例);
服務(wù)端實現(xiàn)該AIDL文件生成的Java接口(系統(tǒng)會自動生成對應的Java接口);
客戶端連接綁定該遠程服務(wù)。
①創(chuàng)建AIDL文件
創(chuàng)建一個 AIDL 文件,聲明服務(wù)端要暴露給客戶端的接口,然后創(chuàng)建一個 Service 監(jiān)聽客戶端的連接請求,并在其中實現(xiàn) AIDL 文件中的接口
注意,為了方便開發(fā),我們一般把 AIDL 相關(guān)的文件放在同一包中,這樣當客戶端是另一個應用時可方便的把整個包復制到客戶端工程中
首先了解下 AIDL 文件支持的幾種數(shù)據(jù)類型:
- 基本數(shù)據(jù)類型
- String、CharSequence
- ArrayList、HashMap,其內(nèi)部元素也需要被AIDL支持
- 實現(xiàn)了 Parcelable 接口的對象
- AIDL 類型的接口,非普通接口
Book是實現(xiàn)了Parcelable的類,只定義了name字段,按照規(guī)定如果 AIDL 文件用到了自定義Parcelable對象,同時需要提供一個Book.aidl文件
- package com.json;
- parcelable Book;
②ILibraryManager.aidl定義了服務(wù)端要暴露給客戶端的接口:
- package com.json;
- import com.json.Book;
- interface ILibraryManager{
- // 近期新書查詢
- List<Book> getNewBookList();
- // 圖書捐贈
- void donateBook(in Book book);
- }
③接下來就是AIDL文件生成的Java接口,在編寫服務(wù)類前要先編譯項目,這樣在服務(wù)類里使用 AIDL 生成的java類
首先通過ILibraryManager.Stub()創(chuàng)建一個mBinder對象,并實現(xiàn)了ILibraryManager.aidl中定義的接口方法,在onBind()方法中返回創(chuàng)建的mBinder,并在服務(wù)onCreate()添加數(shù)據(jù)。
最后在 AndroidManifest.xml 注冊服務(wù):
- <service
- android:name=".ManagerService"
- android:process=":remote">
- </service>
- public class ManagerService extends Service {
- private static final String TAG = "ManagerService";
- // CopyOnWriteArrayList 支持并發(fā)讀寫
- 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"));
- }
- }
④客戶端連接綁定該遠程服務(wù)
先實現(xiàn)ServiceConnection接口,在onServiceConnected()方法中將IBinder對象轉(zhuǎn)換成ILibraryManager對象,通過該對象就能調(diào)用ILibraryManager.aidl中聲明的方法了。
接下來綁定服務(wù):
- 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的通知功能
首先創(chuàng)建一個服務(wù)端通知客戶端的 IOnNewBookArrivedListener.aidl 接口:
- package com.json;
- import com.json.Book;
- interface IOnNewBookArrivedListener {
- void onNewBookArrived(in Book book);
- }
服務(wù)端要先注冊后才能收到通知,同時也可以取消注冊,所以要給之前的ILibraryManager.aidl添加連個方法了:
- interface ILibraryManager{
- ......
- // 注冊通知
- void register(IOnNewBookArrivedListener listener);
- // 取消注冊
- void unregister(IOnNewBookArrivedListener listener);
- }
修改ManagerService:
- public class ManagerService extends Service {
- ......
- ......
- ......
- // 系統(tǒng)提供的專門用于保存、刪除跨進程 listener 的類
- private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
- // AtomicBoolean 支持并發(fā)讀寫
- 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() {
- // 如果服務(wù)還沒終止
- 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類,它是系統(tǒng)提供的專門用于刪除跨進程 listener 的類,用普通的集合難以保證客戶端注冊的 listener 和服務(wù)端存儲的 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 方法在子線程被調(diào)用,所以通過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 的基礎(chǔ)上封裝而成的,可以看做是 AIDL 的簡化版,支持一對多的串行實時通信,
一次只處理一個請求,不存在并發(fā)的問題。和 AIDL 的使用類似,但要簡單的多,同樣需要實現(xiàn)服務(wù)端和客戶端。
首先來看服務(wù)端
- public class MessengerService extends Service {
- private static final String TAG = "MessengerService";
- // 將Messenger和Handler關(guān)聯(lián)起來
- 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;
- }
- }
- }
- }
看客戶端的實現(xiàn)
- 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);
- // 傳遞服務(wù)端回復客戶端時需要使用的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()中,將服務(wù)端的Binder轉(zhuǎn)換成服務(wù)端的Messenger對象,然后發(fā)送消息,由于服務(wù)端還需要給客服端回復消息,所以需要在客戶端創(chuàng)建一個Messenger對象附加在消息上發(fā)送給服務(wù)端使用
多進程總結(jié):
1、多進程app可以在系統(tǒng)中申請多份內(nèi)存,但應合理使用,建議把一些高消耗但不常用的模塊放到獨立的進程,不使用的進程可及時手動關(guān)閉;
2、多進程占用內(nèi)存多,耗電量會增加,這個要注意;
3、每個進程在創(chuàng)建的時候,都會執(zhí)行Application的onCreate進行初始化,如果這時候沒有針對不同進程處理,onCreate的初始化業(yè)務(wù)會被多次執(zhí)行,這是沒有必要的而且多次初始化容易引起問題,所以需根據(jù)不同進程初始化相應的業(yè)務(wù)。
本文轉(zhuǎn)載自微信公眾號「 Android開發(fā)編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系 Android開發(fā)編程公眾號。