標籤:登入 喚醒 後台 ini 二進位協議 二進位 通知 公司 hat
《Android 即時通訊開發小結》基於IM Andriod 開發的各種常見問題,結合網易雲信即時通訊技術的實踐,對IM 開發做一個全面的總結。
相關推薦閱讀:、
Android 即時通訊開發小結(二)
移動IM開發指南1:如何進行技術選型
移動IM開發指南2:心跳指令詳解
移動IM開發指南3:如何最佳化登入模組
用戶端架構
作為一個 IM 軟體,最重要的一個特性就是保證訊息的達到率和即時性。達到率受伺服器效能和設計協議影響,後面再談。而即時性則主要取決於用戶端進程是否長期存活,串連是否一致保持。
進程切分
在 Android 系統中,App 對於自己應用的生命週期是基本沒有控制力,系統能在任意時候將你的進程殺死,且不會發出任何通知,也會在它認為合適的時候把你叫起來。進程前後台切換也同樣不會給出任何通知。不過進程的生死控制也還是有一些規矩的,大體上來說就是進程占的資源越多(記憶體,CPU 時間等等),對於使用者越不重要(前台進程->可視進程->服務進程->後台進程->空進程),越容易被幹掉。因此,進程應當盡量小巧,且具有高的優先順序。
如果一個應用本身就很小巧的話,一個進程就完全足夠了,主線程負責 UI,另起一個後台線程跑一個服務。而如果應用比較龐大的話,將推送服務獨立出來則是一個更好的選擇。主進程負責使用者互動和主要的商務邏輯,佔用龐大的資源,當退到後台後,隨時被殺死都無所謂。推送進程則僅僅負責與伺服器互動,保持最小限度的商務邏輯處理。
網路連接和登入狀態是綁在一起的,登入之後,同步資料也是必須的操作。因此,登入和同步資料都需要在推送進程中完成,除此之外,其他的業務都交給 UI 進程處理。推送進程收到自己不屬於自己的協議時,就將資料扔給 UI 進程處理。
兩個進程之間通訊方式沒有別的選擇,只有 AIDL,痛點在於介面的設計。IM 商務邏輯複雜,我們不可能為每一個調用實現一個 AIDL 介面,因此肯定會把介面調用打包成控制命令傳遞。而標識控制命令比較容易想到的方式,是採用類似於 Message 的 what,由我們為每一個控制命令分配一個命令號(或者再加一個子命令號),並指定對應的命令資料格式,接收端根據命令號再將資料反解出來處理。這種方式比較麻煩,且可維護性很差。更優雅的方式是使用遠端程序呼叫,發送端申明業務的調用介面,並在遠端實現這些介面,當發送端調用這些介面時,遠端直接調用對應介面的實現。除了使用各種第三方架構外,Java 自身的 Proxy 也能實現這個功能。而從推送進程到 UI 進程還有一點不同,UI 進程隨時可能會被幹掉,AIDL 調用可能會返回失敗,此種情況可選擇 Intent 方式傳遞資料,併兼具喚起 UI 進程的功能。
保 活
保活分為三個方面,一是系統API提供了介面,應用自己就能做的,這是”合法“的,二是利用系統的缺陷,躲開系統的審查,這算是”非法“的,或者是”灰色“的,三就是多個 App 結盟,互相喚醒,這是耍流氓,誰的陣營龐大誰就贏。
第一種主要有系統鬧鐘,各種事件的 BroadcastReceiver,任務被移除的回調通知等。
第二種已知的就是在 4.4 及以前版本上,使用 native 進程,並將該進程從 davilk 父進程中脫離,掛接到 init 進程上,以此避開系統的查殺。然後在這個 native 進程中,定時喚起應用。為了讓這個 native 進程更輕巧,可以使用 exec 的方式啟動一個可執行檔,以除掉直接 fork 帶入的 Zygote 進程環境。另外,這種方式也被用在監聽自己應用被卸載時彈出調查視窗。
第三種方式現在各大互連網公司都在使用,方式很簡單,互相調用指定的 Service,或髮指定得廣播即可。只要你起一個阿里系的 App,其他阿里系的 App 都會被跟著喚起。你啟動一個裝了友盟 SDK 的 App, 其他裝了友盟 SDK 的 App,以及阿里系的 App 都會被跟著喚醒。
通常,第一種是必備,第二種和第三種則會結伴出現,流氓到底。
通訊協定選擇
訊息的即時性的另一個保證是長串連。當然,你也可以用短串連輪詢,但這個一般只在網頁端短時聊天使用,在 Android 後台無限時輪詢沒有人能受的了。長連線類型可以選傳統的 TCP,也可以使用 比較新的 WebSocket。 使用後一種的好處主要是伺服器的,他們一套串連就可以服務好 App 端和 Web 端。
IM 的通訊協定選擇性很多,開源的有 XMPP,MQTT等,使用開源協議的優勢在於上手快,資料多。而大部分主流 IM 則一般會設計私人的通訊協定。使用私人協議,可以針對自己的商務邏輯,設計出更省流量,效率更高的協議,同時,還能有效保護自己的生態圈,就像 Android 手機裝不了蘋果系統,易信使用者不能給使用者發訊息一樣。
私人協議的協議內容和開源協議差不多,可以包含通用的協議頭,然後加上負載包體。打包時,為了追求可讀性,可以使用文本協議,為了追求省流量,則一般使用二進位協議。
在設計私人協議時,訊息必達是一個需要側重考量的地方。由於移動網路的複雜性,訊息在用戶端和伺服器之間傳遞是有很大可能被傳丟的。當用戶端發送訊息給伺服器時,用戶端並不能確保訊息一定就會被伺服器收到,需要伺服器在收到訊息後給用戶端一個回饋,如果用戶端沒有收到回饋,就需要在一定逾時後重新發送。這裡存在一個問題就是有可能伺服器已經收到了,但回饋的包被丟掉了,這時就會造成訊息重複,為了去重,我們需要為相同的訊息分配相同的 uuid,供接收方去重。同樣,當伺服器將訊息轉寄給接收端時,伺服器也不能保證接收端就一定能收到,需要接收端給伺服器一個回執,告訴伺服器這條訊息我已經收到了,你就不要再給我發了。
Android 即時通訊開發小結(二)將會從“建立安全連線”、“心跳”、“斷線重連”、“多媒體資料管理”、“圖片”、“語音”等問題出發,結合網易雲信即時通訊技術的實踐, 進行詳細的介紹和總結。
Android 即時通訊開發小結(一)