1. 程式人生 > >支付寶客戶端架構解析:Android 容器化框架初探

支付寶客戶端架構解析:Android 容器化框架初探

摘要: 本文將介紹支付寶 Android 容器化框架設計的基本思路。

1. 前言

由本章節開始,我們將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。

本節將介紹支付寶 Android 容器化框架設計的基本思路。

1.1 開發背景

隨著 Android 應用程式所能實現的功能越來越強大和複雜,隨之而來的是:

  • Android 程式的的程式碼和資源越來越多,APK 檔案的 size 越來越大,Android 程式也越來越複雜;
  • 隨著應用的迭代、專案的擴張,團隊數量以及團隊人數的同時增多,基於傳統架構模式的並行開發也變得愈加困難。

此外,移動客戶端通常需要面對動態化開發的挑戰;Bug 緊急修復等運維需求;同時也有一些線上運營的需求,如動態下發廣告,推送接入活動等。如果每次有運維、運營需求,都需要一次客戶端發版,那將是傳統的開發人員的夢魘。

Android 開發者們深切體會到一個穩健可靠、可擴充套件的、支援大規模並行開發的客戶端開發框架對於平臺級別的客戶端 App 的重要性。事實上,客戶端框架設計的健壯性和擴充套件性,在面對上述需求和解決困難上,往往能達到事半功倍的效果,尤其是 Android 客戶端開發人員將深受其利。

那麼,作為平臺級別的 Android 客戶端 App 究竟該如何的進行框架設計,才能滿足千變萬化的移動網際網路時代的困難和需求?

1.2 平臺級客戶端框架面臨的問題

No. 問題 描述
1 專案工程複雜度高,開發、編譯、測試、整合都非常困難 支付寶 App 程式碼 200W 行+
2 平臺級App的內部微應用(團隊)非常多,並行開發要求高 內部多達幾十個應用
3 APK size 龐大 支付寶 App60+M,導致在某些廠商 Rom 中安裝不上
4 線上版本出現各種問題 如:發版後,UCSDK 被烏雲平臺暴露出安全漏洞
5 線上活動運營需求 春節紅包掃福活動,預案要動態推送新 so 檔案到客戶端

我們可以歸納為:平臺級客戶端框架必須要解決的是模組化和 動態化這兩大核心問題。

(本篇文章我們著重關注模組化相關內容,後續我們通過其他文章分析 動態化的能力。)

1.3 框架設計原則

為了解決上述模組化的問題,我們要遵循以下原則去設計客戶端框架:

  • 根據基礎技術層級、客戶端的業務線等原則,對客戶端應用程式進行模組化拆分。
  • 每一個模組由獨立的小團隊或者個人來進行開發、維護、測試、整合。
  • 模組與模組之間要做到徹底解耦,模組之間可以通過介面進行依賴。
  • 每一個模組可以進行熱插拔,單個模組的插拔不影響整體的工程的編譯執行。

2. Quinox 簡介

Quinox 客戶端框架是類 OSGi( like-as)框架的實現。Quinox 一詞來源於著名的 OSGi 框架的實現 Equinox。

基於此框架的客戶端 App,都是由一個個的積木搭建而成,這些積木被稱之為:Bundle。

bundle

3. Bundle 介紹

3.1 什麼是 Bundle

Bundle 是 OSGi 規範的模組化基本單位,與 Android 裡的 android.os.Bundle 是兩個完全不同的概念。
OSGi 裡的 Bundle 指的是 Java 應用程式的基本單位,它是一個模組單元(Jar 格式),也是上文 Quinox 簡介裡提到的積木。
基於 Quinox 容器框架開發的應用程式也是由眾多的 Bundle(APK 格式)構成。

本章節將從專案開發的三個不同的時期對 Bundle 的形態進行闡述:

時期 形態
開發期 Bundle 工程
構建期 Bundle 包
整合期 整合客戶端的 Bundle 基線

3.2 Bundle 工程

常規的 Android 專案開發,程式碼工程通常有兩種(兩級)型別

工程型別 Library Application
工程輸出 Aar Apk

基於 Quinox 容器框架開發的 Android 專案,程式碼工程則有三種(三級)型別

工程型別 Library Bundle(工程包) Application(測試包/安裝包/Final APK)
工程輸出 Aar Apk(.jar) Apk

關於 Bundle 工程,我們需要了解以下三點:

  • Bundle 工程跟常規的 Android Application 工程非常的類似:它內部也會有多個 Library(Android Module);它的輸出形式也是 APK 格式。
  • 雖然 Bundle 包檔案本質上是 APK 格式,但是該 APK 是無法執行的。同時,Bundle 工程被 deploy 到 mvn 倉庫裡時,它的字尾名是會改為.jar。
  • 基於 Quinox 容器的 Application 工程(可稱之為 Portal 工程)則是將眾多 Bundle(APK)合併成一個 APK(Final)的過程。這裡是合併,而不是編譯,所以生成最終 APK 的速度將會非常快,因為編譯已經被分散式的進行在各個 Bundle 中了。基於 Quinox 容器開發的客戶端程式,需使用 mPaaS 定製的構建工具(即打包外掛)。

關於 Bundle 工程的結構圖請參考:

3.3 Bundle 包

如上所述,Bundle 工程的輸出也是 APK 檔案。

在 OSGi 規範中,Bundle 是有很多屬性的。Bundle 工程輸出的 APK 與常規的 APK 有一個不同點,mPaaS 外掛會將 Bundle 的所有屬性生成一個特殊的檔案放在這個 APK 中,供容器去解讀。

除了 APK 檔案之外,Bundle 工程的構建結果還包含:

  • AndroidMannifest.xml
  • Bundle 介面包(可以理解為一個 jar 包,它包含且暴露該 Bundle 提供的介面類)
  • mapping.txt

Bundle 包檔案,在構建完成之後,通常要 deploy 到本地/遠端的 mvn 倉庫中,以供其他 Bundle 工程引用,或是被 Portal 工程整合。

3.4 Bundle 基線

前面已經講述過,構建 Final APK 其實主要就是將很多的 Budnle APK 合併成最終的 APK 的過程,而這些眾多的 Bundle APK 們都存放於 mvn 倉庫中。

因此我們將這些 Bundle 的 GAV(GroupId,ArtifactId,Vesion)的集合,稱之為基線。

當某一個團隊/個人開完一個 Bundle 工程的新功能,並經過測試達到可釋出狀態,就可以更新基線裡的版本號。我們將這個過程稱之為進基線。我們認為:基線裡打出來的 APK 是穩定可執行的;沒有穩定 Bundle 工程包不應該進基線。

Bundle 基線機制可以很好的隔離了模組之間的相互影響,保障了不同團隊間開發環境的和諧與穩定,達到了我們之前的設計的初衷,因此可以很好的支援多團隊並行開發。

3.5 Bundle 包屬性及配置辦法

關於 Bundle 屬性,我們可以參考 OSGi 的 Bundle 屬性。Quinox 容器框下定義的 Bundle 屬性要簡單的多。

下表將列舉 Bundle 的所有屬性以及配置方法:

名稱 說明 配置辦法
Bundle-Name Bundle 的名稱,作為 key 值存在。同一個客戶端 apk 中,不允許同名的 Bundle 存在 由 mPaaS 外掛根據 Bundle 工程的 GAV 的 GroupId、ArtifactId ,以一定的規則生成而來。
Bundle-Version Bundle 的版本號,各個 Bundle 的介面包必須做到 API 版本向下相容。 由 mPaaS 外掛根據 Bundle 工程的 GAV 中的 Version 生成而來
Init-Level 已廢棄 配置為 1 即可
Package-Name 已廢棄 配置為 '' 即可
Component-Name Bundle 中宣告的 Android Component。它跟 Export-Pacakges 屬性一樣,是 Bundle 的入口類。 由 mPaaS 外掛根據 AndroidManifest.xml 檔案中定義的 Activity,Service,BroadcastReceiver,ContentProvider 等生成
Package-Id Bundle 工程的資源的 packageid,具體技術細節請參考4.2章節 必須由開發同學在 Bundle 工程中設定屬性 packageId,其值的設定區間為【27, 127】,如果沒有資源,則設定為 127,如果 Bundle 為 Bundle 依賴關係樹上根節點的 Bundle,則設定為27。
Contains-Dex 此 Bundle 中是否包含程式碼(classes.dex) 由 mPaaS 外掛根據 Bundle 檔案中是否包含 classes.dex 節點判斷得來。備註:靜態連結的 Bundle 由於 classes.dex merge 到了主 apk 中,所以該屬性會被修正為 false
Contains-Res 此 Bundle 中是否包含資源(resources.arsc) 由 mPaaS 外掛根據 Bundle 檔案中是否包含 resources.arsc 檔案判斷得來。
Native-Library 此 Bundle 中是否包含 native so(lib/xxx/libxxx.so) 由 mPaaS 外掛根據 Bundle 檔案中 native so 檔案判斷得來。備註:在構建最終 apk 時,Bundle 中所有的 so 檔案在構建 Final apk 時,會 merge 到 Final apk 中,所以該屬性會被修正為 null
Required-Bundle 此 Bundle 依賴的 Bundle 列表:為 [email protected] 的格式。 由 mPaaS 外掛根據Bundle專案工程依賴其他Bundle介面包,來生成此屬性。
Export-Pacakges Bundle 匯出包(請參考 OSGi 的匯出包概念)。Quinox 容器將根據匯出包,從對應的 Bundle 中載入類。 必須由開發同學在 Bundle 工程中設定屬性 exportPackages。例如:某個非靜態連線的 Bundle 提供了類: com.alipay.android.phone.framework.api.A作為介面給其他 Bundle 使用,則須將com.alipay.android.phone.framework.api配置為匯出包。(反射被使用的類也應納入匯出包)。有多個匯出包的用','隔開。為了效能考慮,匯出包不應設定太多,或者範圍太廣。

4. 資源管理

4.1 資源管理器

作為 Android 開發人員,我們知道通過 android.content.res.Resources 物件可以獲取字串、佈局、圖片、動畫等資源。

在 Quinox 容器化的框架內,原生的資源管理肯定無法實現多 Bundle 的資源管理,這時候,我們就構建了 Bundle 資源管理器,來專門處理各個 Bundle 的資源的載入、呼叫等工作,替代了 Android 原生的資源管理邏輯。

但是,由於所有 Bundle 包都是獨立編譯的,它們中的資源極可能存在著相同的資源 id。因此,當存在相同資源時,就可能存在衝突,那麼如何解決資源 id 的衝突呢?

4.2 資源 id

作為 Android 程式設計師,我們都知道資源 id 是一個 int 值,它包含4個 byte。它是在構建 APK 工程時,由 aapt 工具生成,定義在 R 檔案中。

示例程式碼:

public final class R {
    public static final class drawable {
        public static int xxx_bg=0x1e020000;
    }
    public static final class id {
        public static int xxx_id=0x1e050001;
    }
    public static final class layout {
        public static int xxx_layout=0x1e030000;
    }
    public static final class string {
        public static int xxx_str=0x1e040001;
    }
}

這四個 byte 含義如下:

  • 第一個位元組為:pacakgeId。
  • 第二個位元組為:typeId。它表示的是不同的資源型別,如字串,佈局,圖片,動畫等。
  • 第三第四兩個位元組合起來為:資源名稱的 id

如下圖所示:

到這裡,很多讀者應該已經理解到了,Quinox 容器框架關於資源 id 衝突的解決方案是,讓 mPaaS 打包外掛使用改造過 aapt 工具,對每一個 Bundle 工程都指定不同 packageId,進行分割槽隔離,從而確保不同的 Bundle 之間資源 id 是不會重複的。這也是為什麼 Bundle 工程裡需要指定 packageId 的緣故。

5.容器化

關於 Quinox 容器化這裡,由於目前為止,Quinox 暫未開源,所以本章節內,我們暫時不涉及到原始碼分析。

上面我們聊了很多關於 Bundle 的話題,那麼整個容器化的核心,也是如何管理各個 Bundle。這時就要引出我們的容器管理器了,容器管理器的主要工作就是協調各個 Bundle,對各種資訊進行增刪改查。

在應用啟動後,我們的容器管理器會讀取配置資訊,生成各 Bundle 的資訊例項。

5.1 容器管理器:增、刪

Quinox 容器框架的目標是解決 Android 客戶端 App 模組化和動態化這兩大核心問題。增、刪這兩項能力,更多的是用來實現動態化能力的,方便容器對各個 Bundle 進行動態新增、刪除。由於本文著重描寫模組化的能力,所以這部分,我們後續單開專題來分析容器的動態化能力。

5.2 容器管理器:改

關於容器管理器的改的能力,Quinox 主要是利用改的能力,做一些啟動效能的優化,以及機型適配上的工作,這裡涉及原始碼較多,我們不做過多分析。

5.3 容器管理器:查

容器管理器使用最頻繁的功能介面應該是查詢介面:

  • 根據 BundleName 進行查詢(還記得 Bundle 的 Require-Bundle 屬性麼?)
  • 根據 packageId 進行查詢(非27的 Bundle)
  • 根據 Android Component 類名進行查詢(還記得 Bundle 的 Component-Name 屬性麼?)

通過管理器的查詢介面,我們進行各個 Bundle 之間的協調、通訊,完成容器化的功能。

5.4 元件的啟動

除了容器管理器,還有一個重要的點就是元件的啟動器。Quinox 容器定製類原生 Android Activity 的啟動流程,從而自主管理 Activity 的建立以及生命週期。

同時,由於 Activity 是我們自主的啟動器進行的建立,我們還可以對 Activity 進行一些定製化的改造,方便其更好的適配容器這套體系。比如說給 Activity 賦予我們自定義的資源管理器,管理 Activity 堆疊並對外提供介面,對 Activity 各生命週期做一些切面工作等等。

定製化的元件啟動器,還有一個好處就是可以做到 Activity 的動態執行。所謂動態執行,是指執行出廠未註冊在 Manifest 中的 Activity。這塊功能,更多是為了支援容器動態化的能力。

由於 Activity 動態執行的實現邏輯涉及較多的核心技術點,所以我們暫時不進行具體實現的剖析。

6. 小結

通過本節內容,我們已經初步瞭解了 mPaaS 在安卓端容器化框架的設計思路和相應模組。由於篇幅限制,很多技術要點我們無法一一展開。


本文作者:josephjin

閱讀原文

相關推薦

支付客戶架構解析Android 容器框架初探

摘要: 本文將介紹支付寶 Android 容器化框架設計的基本思路。 1. 前言 由本章節開始,我們將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診

支付客戶架構解析iOS 容器框架初探

1. 前言 由本章節開始,我們將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。 本節將介紹支付寶 iOS 容器化

支付客戶架構解析Android 客戶啟動速度優化之「垃圾回收」

前言 《支付寶客戶端架構解析》系列將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。 本節將介紹支付寶 Andro

支付客戶架構解析iOS 客戶啟動效能優化初探

前言 《支付寶客戶端架構解析》系列將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。 啟動應用是使用者使用任何一款

支付客戶架構分析自動化日誌收集及分析

前言 《支付寶客戶端架構解析》系列將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。 本節將結合禾兮

支付 App 構建優化解析Android 包大小極致壓縮

前言 本章節我們將圍繞《支付寶 App 構建優化解析》另啟新系列,細分拆解客戶端在“程式碼管理”、“證書管理”、“版本管理”、“構建打包”等維度的具體實現方案展開討論,帶領大家進一步瞭解支付寶在 App 構建模組下的持續優化。 本節將主要記錄通過對支付寶 Android 包大小進行壓縮,來改善執行效率和質

支付 App 構建優化解析通過安裝包重排布優化 Android 啟動效能

1. 前言 本章節我們將圍繞《支付寶 App 構建優化解析》另啟新系列,細分拆解客戶端在“程式碼管理”、“證書管理”、“版本管理”、“構建打包”等維度的具體實現方案展開討論,帶領大家進一步瞭解支付寶在 App 構建模組下的持續優化。 本節將主要記錄通過對支付寶 Android Apk 檔案的重新佈局,來改

iOS支付功能篇原生WebView調起支付客戶支付方案

產品需求: 使用iOS原生WKWebView載入H5調起支付寶客戶端進行支付的功能實現。 資源 開發歷程 1. 安卓直接webView載入上面的URL直接可完成跳轉支付寶彈出支付介面; 2. iOS WKWebVie

如何在自己的應用中開啟支付客戶

在做Android程式的過程中,我們經常需要在自己的專案中開啟其他軟體,通用的方法如下: 1.在程式你中通過程式碼來檢視手機上已安裝的軟體的包名; 2.通過該應該的包名來開啟該軟體。 獲取已安裝軟體的包名的程式碼如下: PackageMa

mui掉支付客戶

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>支付方式</title><meta name="viewport"

開源Android容器框架Atlas開發者指南

隨著手機淘寶業務的快速增長,協作研發團隊的不斷擴大,技術也面臨著更多挑戰:一方面程式碼量快速上升導致方法過多,限制打包的正常輸出;另一個方面各業務線的開發和整合都需要到一個 apk上,業務間相互耦合嚴重,整合開發工作效率低下,這個大背景下,急需要做一次從構建,整合,交付的技術

最全最強解析支付錢包系統架構內部剖析(架構圖)

它的 action cer www. per 實現 zookeeper body 高性能 原文地址:http://www.woshipm.com/pmd/160822.html+ 支付寶系統架構概況 典型處理默認 資金處理平臺 財務會計 支付清算 核算中心 交

手機淘Android客戶架構

手機淘寶Android客戶端有幾百人開發,十幾個團隊。如果整個Android客戶端是一個工程,那十幾個團隊每個人上午上班第一件事情估計就是合程式碼,運氣不好,一天都在合程式碼,而且只要有一個人提交的程式碼編譯不過,所有人都會被堵塞在那裡,所以單個工程是不可能的事情。  

深入解析Diamond之客戶架構

一、什麼是Diamond diamond是淘寶內部使用的一個管理持久配置的系統,它的特點是簡單、可靠、易用,目前淘寶內部絕大多數系統的配置,由diamond來進行統一管理。 diamond為應用系統提供了獲取配置的服務,應用不僅可以在啟動時從diamond獲取相關的配置,而且可以在執行中對配置資料的

React Native (IOS和Android) 支付和微信支付整合實戰(支付服務篇)

序言:React Native無論是在社群和應用程度上,在國內外是十分廣泛和普及的。而支付寶和微信在支付模組上都有或多或少的支援,雖然沒有完整的Demo,不過在我做過一個相關整合的專案後,在此我把相關的步驟和方法總結出來和大家分享,希望能夠幫助大家少走彎路,快速整合。 支

Zookeeper之Zookeeper底層客戶架構實現原理(轉載)

一次 描述 綁定 機制 一個 ini fin 源碼 receive Zookeeper的Client直接與用戶打交道,是我們使用Zookeeper的interface。了解ZK Client的結構和工作原理有利於我們合理的使用ZK,並能在使用中更早的發現問題。本文將在研究源

在ADUC中設置登錄到工作站功能,導致遠程桌面訪問客戶 報錯系統管理員已經限制你登錄的計算機

登錄 遇到 解決 nag net 計算機 preview 錯誤 報錯 在ADUC中設置登錄到工作站功能 遠程時錯誤提示:系統管理員已經限制你登錄的計算機。請在其他計算機上嘗試登錄。如果問題依然存在,請與系統管理員活技術支持聯系 經過我的研究,有兩種解決方案。 1 關閉nla

驗證客戶的合法性

處理 .com 得到 加密算法 提前 send class code 不同 通常會用hmac模塊,和hashlib中的加鹽加密算法類似 服務器端提前和客戶端約定好key ,然後可以用os模塊裏的urandom來生成一組隨機的字節 把隨機字節發送給客戶端去加密處理 ,服務器

《Spring Cloud微服務實戰》讀書筆記之客戶負載均衡Spring Cloud Ribbon - 4

摘要 客戶端負載均衡元件部署在客戶端,由客戶端維護要訪問的服務端清單(清單來源於服務註冊中心)。在Spring Cloud 中預設使用Ribbon作為客戶端負載均衡。 關鍵詞:客戶端負載均衡,Ribbon 一、什麼是客戶端負載均衡 負載均衡是對系統高可用、緩解網路壓力、處理能力擴容的重要手段之一。通常

PHP 判斷客戶是IOS還是Android

public function test(){ if(strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone')||strpos($_SERVER['HTTP_USER_AGENT'], 'iPad')){ echo 'systerm i