日韩国产精品99成人不卡在线无毒|狠狠躁夜夜爽一级二级精品|亚洲日日噜噜孕妇中文字幕|日韩久草中文三级片

新聞動態(tài)

巧用Android多進程,微信,微博等主流App都在用

網站建設 發(fā)布者:cya 2019-12-04 08:36 訪問量:351

作者:nanchen2251

博客:https://juejin.im/post/5d2dc5e95188257b775d3e40


目錄


  1. 前言

  2. 為什么要使用多進程?

  3. 為什么需要“跨進程通訊”?

  4. 跨進程通訊的方式有哪些?

  5. 使用AIDL實現一個多進程消息推送

  6. 實現思路

  7. 例子具體實現

  8. 知其然,知其所以然。

  9. 跨進程的回調接口

  10. DeathRecipient

  11. 權限驗證

  12. 根據不同進程,做不同的初始化工作

  13. 總結

  14. 結語


為什么要使用多進程


對于進程的概念,來到這里的都是編程修仙之人,就不再啰嗦了,相信大家倒著、跳著、躺著、各種姿勢都能背出來。


相信很多同學在實際開發(fā)中,基本都不會去給app劃分進程,而且,在Android中使用多進程,還可能需要編寫額外的進程通訊代碼,還可能帶來額外的Bug,這無疑加大了開發(fā)的工作量,在很多創(chuàng)業(yè)公司中工期也不允許,這導致了整個app都在一個進程中。


整個app都在一個進程有什么弊端?

在Android中,虛擬機分配給各個進程的運行內存是有限制值的(這個值可以是32M,48M,64M等,根據機型而定),試想一下,如果在app中,增加了一個很常用的圖片選擇模塊用于上傳圖片或者頭像,加載大量Bitmap會使app的內存占用迅速增加,如果你還把查看過的圖片緩存在了內存中,那么OOM的風險將會大大增加,如果此時還需要使用WebView加載一波網頁,我就問你怕不怕!


微信,微博等主流app是如何解決這些問題的?

微信移動開發(fā)團隊在 《Android內存優(yōu)化雜談》 一文中就說到:“對于webview,圖庫等,由于存在內存系統(tǒng)泄露或者占用內存過多的問題,我們可以采用單獨的進程。微信當前也會把它們放在單獨的tools進程中”。


下面我們使用adb查看一下微信和微博的進程信息(Android 5.0以下版本可直接在“設置 -> 應用程序”相關條目中查看):

進入adb shell后,使用 “ps | grep 條目名稱” 可以過濾出想要查看的進程。

可以看到,微信的確有一個tools進程,而新浪微博也有image相關的進程,而且它們當中還有好些其它的進程,比如微信的push進程,微博的remote進程等,這里可以看出,他們不單單只是把上述的WebView、圖庫等放到單獨的進程,還有推送服務等也是運行在獨立的進程中的。一個消息推送服務,為了保證穩(wěn)定性,可能需要和UI進程分離,分離后即使UI進程退出、Crash或者出現內存消耗過高等情況,仍不影響消息推送服務。


可見,合理使用多進程不僅僅是有多大好處的問題,我個人認為而且是很有必要的。


所以說,我們最好還是根據自身情況,考慮一下是否需要拆分進程。這也是本文的初衷:給大家提供一個多進程的參考思路,在遇到上述問題和場景的時候,可以考慮用多進程的方法來解決問題,又或者,在面試的時候,跟面試官聊到這方面的知識時候也不至于尷尬。


為什么需要“跨進程通訊”


Android的進程與進程之間通訊,有些不需要我們額外編寫通訊代碼,例如:把選擇圖片模塊放到獨立的進程,我們仍可以使用startActivityForResult方法,將選中的圖片放到Bundle中,使用Intent傳遞即可。(看到這里,你還不打算把你項目的圖片選擇弄到獨立進程么?


但是對于把“消息推送Service”放到獨立的進程,這個業(yè)務就稍微復雜點了,這個時候可能會發(fā)生Activity跟Service傳遞對象,調用Service方法等一系列復雜操作。


由于各個進程運行在相對獨立的內存空間,所以它們是不能直接通訊的,因為程序里的變量、對象等初始化后都是具有內存地址的,舉個簡單的例子,讀取一個變量的值,本質是找到變量的內存地址,取出存放的值。不同的進程,運行在相互獨立的內存(其實就可以理解為兩個不同的應用程序),顯然不能直接得知對方變量、對象的內存地址,這樣的話也自然不能訪問對方的變量,對象等。此時兩個進程進行交互,就需要使用跨進程通訊的方式去實現。簡單說,跨進程通訊就是一種讓進程與進程之間可以進行交互的技術。


跨進程的通訊方式有哪些


  1. 四大組件間傳遞Bundle;

  2. 使用文件共享方式,多進程讀寫一個相同的文件,獲取文件內容進行交互;

  3. 使用Messenger,一種輕量級的跨進程通訊方案,底層使用AIDL實現(實現比較簡單,博主開始本文前也想了一下是否要說一下這個東西,最后還是覺得沒有這個必要,Google一下就能解決的問題,就不啰嗦了);

  4. 使用AIDL(Android Interface Definition Language),Android接口定義語言,用于定義跨進程通訊的接口;

  5. 使用ContentProvider,常用于多進程共享數據,比如系統(tǒng)的相冊,音樂等,我們也可以通過ContentProvider訪問到;

  6. 使用Socket傳輸數據。


接下來本文將重點介紹使用AIDL進行多進程通訊,因為AIDL是Android提供給我們的標準跨進程通訊API,非常靈活且強大(貌似面試也經常會問到,但是真正用到的也不多…)。上面所說的Messenger也是使用AIDL實現的一種跨進程方式,Messenger顧名思義,就像是一種串行的消息機制,它是一種輕量級的IPC方案,可以在不同進程中傳遞Message對象,我們在Message中放入需要傳遞的數據即可輕松實現進程間通訊。但是當我們需要調用服務端方法,或者存在并發(fā)請求,那么Messenger就不合適了。而四大組件傳遞Bundle,這個就不需要解釋了,把需要傳遞的數據,用Intent封裝起來傳遞即可,其它方式不在本文的討論范圍。


下面開始對AIDL的講解,各位道友準備好渡劫了嗎?


使用AIDL使用一個跨進程消息推送


像圖片選擇這樣的多進程需求,可能并不需要我們額外編寫進程通訊的代碼,使用四大組件傳輸Bundle就行了,但是像推送服務這種需求,進程與進程之間需要高度的交互,此時就繞不過進程通訊這一步了。
下面我們就用即時聊天軟件為例,手動去實現一個多進程的推送例子,具體需求如下:


  1. UI和消息推送的Service分兩個進程;


  2. UI進程用于展示具體的消息數據,把用戶發(fā)送的消息,傳遞到消息Service,然后發(fā)送到遠程服務器;


  3. Service負責收發(fā)消息,并和遠程服務器保持長連接,UI進程可通過Service發(fā)送消息到遠程服務器,Service收到遠程服務器消息通知UI進程;


  4. 即使UI進程退出了,Service仍需要保持運行,收取服務器消息。


實現思路


先來整理一下實現思路:

  1. 創(chuàng)建UI進程(下文統(tǒng)稱為客戶端);


  2. 創(chuàng)建消息Service(下文統(tǒng)稱為服務端);


  3. 把服務端配置到獨立的進程(AndroidManifest.xml中指定process標簽);


  4. 客戶端和服務端進行綁定(bindService);


  5. 讓客戶端和服務端具備交互的能力。(AIDL使用);


例子具體實現

為了閱讀方便,下文中代碼將省略非重點部分,可以把本文完整代碼Clone到本地再看文章:

https://github.com/V1sk/AIDL


Step0. AIDL調用流程概覽


開始之前,我們先來概括一下使用AIDL進行多進程調用的整個流程:


  1. 客戶端使用bindService方法綁定服務端;


  2. 服務端在onBind方法返回Binder對象;


  3. 客戶端拿到服務端返回的Binder對象進行跨進程方法調用;

整個AIDL調用過程概括起來就以上3個步驟,下文中我們使用上面描述的例子,來逐步分解這些步驟,并講述其中的細節(jié)。


Step1.客戶端使用bindService方法綁定服務端


1.1 創(chuàng)建客戶端和服務端,把服務端配置到另外的進程


  1. 創(chuàng)建客戶端 -> MainActivity;


  2. 創(chuàng)建服務端 -> MessageService;


  3. 把服務端配置到另外的進程 -> android:process=”:remote”


上面描述的客戶端、服務端、以及把服務端配置到另外進程,體現在AndroidManifest.xml中,如下所示:

開啟多進程的方法很簡單,只需要給四大組件指定android:process標簽。


1.2 綁定MessageService到MainActivity


創(chuàng)建MessageService:

此時的MessageService就是剛創(chuàng)建的模樣,onBind中返回了null,下一步中我們將返回一個可操作的對象給客戶端。

客戶端MainActivity調用bindService方法綁定MessageService


這一步其實是屬于Service組件相關的知識,在這里就比較簡單地說一下,啟動服務可以通過以下兩種方式:


  1. 使用bindService方法 -> bindService(Intent service, ServiceConnection conn, int flags);


  2. 使用startService方法 -> startService(Intent service);


bindService & startService區(qū)別:
使用bindService方式,多個Client可以同時bind一個Service,但是當所有Client unbind后,Service會退出,通常情況下,如果希望和Service交互,一般使用bindService方法,使用onServiceConnected中的IBinder對象可以和Service進行交互,不需要和Service交互的情況下,使用startService方法即可。


正如上面所說,我們是要和Service交互的,所以我們需要使用bindService方法,但是我們希望unbind后Service仍保持運行,這樣的情況下,可以同時調用bindService和startService(比如像本例子中的消息服務,退出UI進程,Service仍需要接收到消息),代碼如下:

Stpe2.服務端在onBind方法返回Binder對象


2.1 首先,什么是Binder?


要說Binder,首先要說一下IBinder這個接口,IBinder是遠程對象的基礎接口,輕量級的遠程過程調用機制的核心部分,該接口描述了與遠程對象交互的抽象協(xié)議,而Binder實現了IBinder接口,簡單說,Binder就是Android SDK中內置的一個多進程通訊實現類,在使用的時候,我們不用也不要去實現IBinder,而是繼承Binder這個類即可實現多進程通訊。


2.2 其次,這個需要在onBind方法返回的Binder對象從何而來?


在這里就要引出本文中的主題了——AIDL
多進程中使用的Binder對象,一般通過我們定義好的 .adil 接口文件自動生成,當然你可以走野路子,直接手動編寫這個跨進程通訊所需的Binder類,其本質無非就是一個繼承了Binder的類,鑒于野路子走起來麻煩,而且都是重復步驟的工作,Google提供了 AIDL 接口來幫我們自動生成Binder這條正路,下文中我們圍繞 AIDL 這條正路繼續(xù)展開討論(可不能把人給帶偏了是吧)


2.3 定義AIDL接口


很明顯,接下來我們需要搞一波上面說的Binder,讓客戶端可以調用到服務端的方法,而這個Binder又是通過AIDL接口自動生成,那我們就先從AIDL搞起,搞之前先看看注意事項,以免出事故:


AIDL支持的數據類型:


  • Java 編程語言中的所有基本數據類型(如 int、long、char、boolean 等等)


  • String和CharSequence


  • Parcelable:實現了Parcelable接口的對象


  • List:其中的元素需要被AIDL支持,另一端實際接收的具體類始終是 ArrayList,但生成的方法使用的是 List 接口


  • Map:其中的元素需要被AIDL支持,包括 key 和 value,另一端實際接收的具體類始終是 HashMap,但生成的方法使用的是 Map 接口


其他注意事項:


  • 在AIDL中傳遞的對象,必須實現Parcelable序列化接口;


  • 在AIDL中傳遞的對象,需要在類文件相同路徑下,創(chuàng)建同名、但是后綴為.aidl的文件,并在文件中使用parcelable關鍵字聲明這個類;


  • 跟普通接口的區(qū)別:只能聲明方法,不能聲明變量;


  • 所有非基礎數據類型參數都需要標出數據走向的方向標記。可以是 in、out 或 inout,基礎數據類型默認只能是 in,不能是其他方向。


下面繼續(xù)我們的例子,開始對AIDL的講解~


2.4 創(chuàng)建一個AIDL接口,接口中提供發(fā)送消息的方法(Android Studio創(chuàng)建AIDL:項目右鍵 -> New -> AIDL -> AIDL File),代碼如下:

一個比較尷尬的事情,看了很多文章,從來沒有一篇能說清楚in、out、inout這三個參數方向的意義,后來在stackoverflow上找到能理解答案(https://stackoverflow.com/questions/4700225/in-out-inout-in-a-aidl-interface-parameter-value),我翻譯一下大概意思:


被“in”標記的參數,就是接收實際數據的參數,這個跟我們普通參數傳遞一樣的含義。在AIDL中,“out” 指定了一個僅用于輸出的參數,換而言之,這個參數不關心調用方傳遞了什么數據過來,但是這個參數的值可以在方法被調用后填充(無論調用方傳遞了什么值過來,在方法執(zhí)行的時候,這個參數的初始值總是空的),這就是“out”的含義,僅用于輸出。而“inout”顯然就是“in”和“out”的合體了,輸入和輸出的參數。區(qū)分“in”、“out”有什么用?這是非常重要的,因為每個參數的內容必須編組(序列化,傳輸,接收和反序列化)。in/out標簽允許Binder跳過編組步驟以獲得更好的性能。


上述的MessageModel為消息的實體類,該類在AIDL中傳遞,實現了Parcelable序列化接口,代碼如下:

手動實現Parcelable接口比較麻煩,安利一款AS自動生成插件android-parcelable-intellij-plugin
創(chuàng)建完MessageModel這個實體類,別忘了還有一件事要做:”在AIDL中傳遞的對象,需要在類文件相同路徑下,創(chuàng)建同名、但是后綴為.aidl的文件,并在文件中使用parcelable關鍵字聲明這個類“。代碼如下:

對于沒有接觸過aidl的同學,光說就能讓人懵逼,來看看此時的項目結構壓壓驚:

我們剛剛新增的3個文件:

  • MessageSender.aidl -> 定義了發(fā)送消息的方法,會自動生成名為MessageSender.Stub的Binder類,在服務端實現,返回給客戶端調用


  • MessageModel.java -> 消息實體類,由客戶端傳遞到服務端,實現了Parcelable序列化


  • MessageModel.aidl -> 聲明了MessageModel可在AIDL中傳遞,放在跟MessageModel.java相同的包路徑下


OK,相信此時懵逼已解除~


2.5 在服務端創(chuàng)建MessageSender.aidl這個AIDL接口自動生成的Binder對象,并返回給客戶端調用,服務端MessageService代碼如下:

MessageSender.Stub是Android Studio根據我們MessageSender.aidl文件自動生成的Binder對象(至于是怎樣生成的,下文會有答案),我們需要把這個Binder對象返回給客戶端。


2.6 客戶端拿到Binder對象后調用遠程方法


調用步驟如下:


  1. 在客戶端的onServiceConnected方法中,拿到服務端返回的Binder對象;


  2. 使用MessageSender.Stub.asInterface方法,取得MessageSender.aidl對應的操作接口;


  3. 取得MessageSender對象后,像普通接口一樣調用方法即可。


此時客戶端代碼如下:

在客戶端中我們調用了MessageSender的sendMessage方法,向服務端發(fā)送了一條消息,并把生成的MessageModel對象作為參數傳遞到了服務端,最終服務端打印的結果如下:

這里有兩點要說:


  1. 服務端已經接收到客戶端發(fā)送過來的消息,并正確打??;


  2. 服務端和客戶端區(qū)分兩個進程,PID不一樣,進程名也不一樣;


到這里,我們已經完成了最基本的使用AIDL進行跨進程方法調用,也是Step.0的整個細化過程,可以再回顧一下Step.0,既然已經學會使用了,接下來…全劇終。。。

如果寫到這里全劇終,那跟咸魚有什么區(qū)別…


知其然,知其所以然


我們通過上述的調用流程,看看從客戶端到服務端,都經歷了些什么事,看看Binder的上層是如何工作的,至于Binder的底層,這是一個非常復雜的話題,本文不深究。(如果看到這里你又想問什么是Binder的話,請手動倒帶往上看…)


我們先來回顧一下從客戶端發(fā)起的調用流程:


  1. MessageSender messageSender = MessageSender.Stub.asInterface(service);


  2. messageSender.sendMessage(messageModel);


拋開其它無關代碼,客戶端調跨進程方法就這兩個步驟,而這兩個步驟都封裝在 MessageSender.aidl 最終生成的 MessageSender.java 源碼(具體路徑為:build目錄下某個子目錄,自己找,不爽你來打我啊

關鍵字: Android多進程 跨進程通訊 晨展科技

文章連接: http://www.hsjyfc.com.cn/wzjss/631.html

版權聲明:文章由 晨展科技 整理收集,來源于互聯網或者用戶投稿,如有侵權,請聯系我們,我們會立即刪除。如轉載請保留