Skip to content

title: Android 外掛化 author: 書蟲 date: 2022-04-17 16:54:59 tags:

簡介

成為一名優秀的Android開發,需要一份完備的知識體系,在這裡,讓我們一起成長為自己所想的那樣~。

2012年,Android外掛化技術誕生,從最初只支援動態載入Activity到完全模擬app執行時的沙箱系統,歷經了快6年的時間,本文,旨在從全方面的角度解析Android外掛化技術。

實現Android外掛化需要解決的問題

  • 外掛中程式碼的載入和主工程的相互呼叫
  • 外掛中資源的載入和主工程的相互呼叫
  • Android四大元件的生命週期管理

Android外掛化的發展歷程

第一代:dynamic-load-apk和DroidPlugin

dynamic-load-apk: 早期使用ProxyActivity的代理技術,由ProxyActivity去控制外掛Activity的生命週期。 缺點: 每一個Activity都必須繼承外掛Activity,處理Context時必須小心。

DroidPlugin: 透過hook系統服務的方式啟動外掛Activity,來達到和使用普通方式開發app的效果。 缺點: 過多的hook系統服務,這一過程十分複雜且不夠穩定。

第二代:VirtulApk、Small、RePlugin

原理:實現原理上都選擇儘量少的hook,透過在manifest上預埋一些元件實現四大元件的外掛化。其中Small更形成了一個跨平臺、元件化的框架。

第三代:VirtulApp、Atlas

VirtulApp: 能夠完全模擬app的執行環境,能夠實現免安裝應用和雙開技術。 Atlas: 阿里出品,號稱是一個容器化框架,結合了元件化和熱更新技術。

Android外掛化的實現原理

類載入

1.外部apk中的類載入

Android中有兩種類載入器,DexClassLoader和PathClassLoader,它們都繼承於BaseDexClassLoader。

兩者的區別:DexClassLoader多了一個optimizedDirectory的路徑引數,這個目錄必須是內部儲存路徑,用於快取系統建立的Dex檔案。

所以我們可以使用DexClassLoader去載入外部Apk中的類。

2.雙親委託機制

ClassLoader呼叫loadClass方法載入類採用了雙親委託機制來避免重複載入類。 首先,ClassLoader會檢視自身已經載入的類中是否已經存在此類,如不存在,然後,則會使用父類來載入此類,如不能成功載入,則會使用自身過載於BaseDexClassLoader的findClass()方法來載入此類。

DexClass的DexPathList在DexClass的構造器中生成,findClass()方法則是從DexPathList下面找出對應的DexFile,迴圈DexElements,透過dexElement.dexFile取出對應的DexFile,再透過DexFile.loadClassBinaryName()載入對應的類。

單DexClassLoader和多DexClassLoader

作用:使用外掛DexClassLoader載入出需要的類。

1.多DexClassLoader(Replugin)

透過每一個外掛的DexClassLoader載入出自身所需要的類,當每一個外掛需要載入相同的類庫時,可採用該類庫的不同版本來使用。

2.單DexClassLoader(Small)

透過把每一個外掛的pathList(DexFile)合併到主app的DexClassLoader上,來使各個外掛和主app直接能夠相互呼叫類和方法,並且各個外掛中相同的功能可以抽取出來作為一個Common外掛供其它外掛使用。

3.互相呼叫

外掛呼叫主工程

在ClassLoader構造時指定主工程的DexClassLoader為父載入器即可直接呼叫主工程中的類和方法。 主工程呼叫外掛

如果是多DexClassLoader的情況,則需要透過外掛的DexClassLoader載入對應的類並反射呼叫其方法。此種情況,主工程一般會在一個統一的地方對訪問外掛中的類和方法做一些訪問許可權的管理及配置。

如果是單DexClassLoader的情況,則可以直接呼叫外掛中的類和方法。但是當多個外掛引用的庫的版本不同時,會出現錯誤,因此,建議採用Gradle版本依賴管理統一處理主工程及各個外掛的庫依賴。

資源載入

Android透過Resource來載入資源,只要有外掛apk,就可以使用assertManager.addAssertPath(apkPath)的方式來生成assertManager,再使用其new出對應的Resource物件即可。

注意:由於AssertManager並不是Public,所以需要透過反射的方式去呼叫它。並且由於一些Rom對Resource的處理,所以,需要相容處理。

1.資源路徑的處理

有2種處理方式:

合併式:利用assertManager.addAssetPath()將主工程和各個外掛的apk路徑一起加入。 優勢:資源共用。 逆勢:需要處理資源id衝突。

獨立式:主工程和外掛都生成各自獨立的Resource。 優勢:不需要處理資源id衝突。 逆勢:各個外掛需要透過某些方式去獲取其它外掛的Resource。

2.Context的處理

1.建立主工程的Resource 2.hook主工程的Resource 3.將activity與Resource關聯 3.資源衝突

產生的原因:由於主工程和各個外掛引用的Resource id重複產生的衝突。 解決思路:Android中的資源在系統中是以8位16進位制0XPPTTRRRR的方式存在,其中PP即是資源區分的區域(Android系統只用它來區分系統資源和應用資源),只要讓每一個外掛的PP段取不同的值即可解決資源id衝突的問題。 具體解決方式: 1.修改aapt原始碼,編譯期修改PP段。 2.修改Resource的arsc檔案,其中的每一條都包含了資源id和對映路徑。

四大元件支援(Activity)

Activity的處理最為複雜,有兩種處理方式:

1.ProxyActivity的方式。 2.預埋StubActivity,hook系統啟動Activity的過程。

原理:VirtualAPK透過替換了系統的Instrumentation,hook了Activity的啟動和建立,省去了手動管理外掛Activity生命週期的繁瑣,讓外掛Activity像正常的Activity一樣被系統管理,並且外掛Activity在開發時和常規一樣,即能獨立執行又能作為外掛被主工程呼叫。

Android外掛化的發展方向

Android外掛化方向主要有2個方向: 1.結合元件化技術,成為一個大中型app的基礎框架。 2.完全模擬app執行環境的沙盒系統。