Android插件化系列一: 開篇前言,Binder機制,ClassLoader

系列前言

從今天開始,我會花較多的時間來跟你們一塊兒學習Android插件化。這一篇文章是Android插件化的啓動篇。java

Android插件化是以前幾年裏的一個很火的技術概念。從2012年開始就有人在研究這門技術。從粗糙的AndroidDynamicLoader框架,到第一代的DroidPlugin等,繼而發展到第二代的VirtualApk,Replugin等,再到現現在的VirtualApp,Atlas。插件化在國內逐漸的發展和完善,卻也在近幾年出現了RN等替代品之後慢慢會走向弱勢。linux

儘管插件化技術的研究熱潮已通過去,可是這門技術自己仍是有着大量的技術實踐,對於咱們瞭解Android機制頗有幫助。因此從這篇文章開始我會寫一系列的文章,加上本身對插件化的實踐,最後會去從源碼角度分析幾個優秀的插件化庫,造成一套完整的插件化的理論體系。android

下面是插件化的技術框架,也是我這個系列文章的行文思路,git

Android插件化文章框架

一. Binder機制

網上分析Binder機制的文章已經不少了,在這篇文章裏,我不會去講解Binder的使用,而是會去講解清楚Binder的設計思路,設計原理和對於插件化的使用。安全

爲何須要Binder

首先咱們知道,Android是基於Linux內核開發的。對於Linux來講,操做系統爲一個二進制可執行文件建立了一個載有該文件本身的棧,堆、數據映射以及共享庫的內存片斷,還爲其分配特殊的內部管理結構。這就是一個進程。操做系統必須提供公平的使用機制,使得每一個進程能正常的開始,執行和終結。微信

這樣呢,就引入了一個問題。一個進程能不能去操道別的進程的數據呢?咱們能夠想一下,這是絕對不能出現的,尤爲是系統級的進程,若是被別的進程影響了可能會形成整個系統的崩塌。因此咱們很天然的想到,咱們應該把進程隔離起來,linux也是這樣作的,它的虛擬內存機制爲每一個進程分配連續的內存空間,進程只能操做本身的虛擬內存空間。同時,還必須知足進程之間保持通訊的能力,畢竟團結力量大,單憑單個進程的獨立運做是不能撐起操做系統的功能需求的。app

爲了解決這個問題,Linux引進了用戶空間User Space和內核空間Kernel Space的區別。用戶空間要想訪問內核空間,惟一方式就是系統調用。內核空間經過接口把應用程序請求傳給內核處理後返回給應用程序。同時,用戶空間進程若是想升級爲內核空間進程,須要進行安全檢查。框架

補充知識:系統調用主要經過兩個方法:ide

  • copy_from_user():將用戶空間的數據拷貝到內核空間post

  • copy_to_user():將內核空間的數據拷貝到用戶空間

linux系統調用

以上就是linux系統的跨進程通訊機制。而咱們立刻要說的Binder,就是跨進程的一種方式

Binder模型

Binder是一種進程間通訊(IPC)方式,Android常見的進程中通訊方式有文件共享,Bundle,AIDL,Messenger,ContentProvider,Socket。其中AIDL,Messenger,ContentProvider都是基於Binder。Linux系統經過Binder驅動來管理Binder機制。

Binder的實現基於mmap()系統調用,只用拷貝一次,比常規文件頁操做更快。微信開源的MMKV等也是基於此。有興趣的能夠了解一下。

首先Binder機制有四個參與者,Server,Client兩個進程,ServiceManager,Binder驅動(內核空間)。其中ServiceManager和Binder驅動都是系統實現的,而Server和Client是須要開發者本身實現的。四者之中只有Binder驅動是運行在內核空間的。

Binder機制

這裏的ServiceManager做爲Manager,承擔着Binder通訊的創建,Binder的註冊和傳遞的能力。Service負責建立Binder,併爲他起一個字符形式的名字,而後把Binder和名字經過經過Binder驅動,藉助於ServiceManager自帶的Binder向ServiceManager註冊。注意這裏,由於Service和ServiceManager也是跨進程通訊須要Binder,ServerManager是自帶Binder的,因此相對ServiceManager來講Service也就至關於Client了。

Service註冊了這個Binder之後,Client就能經過名字得到Binder的引用了。這裏的跨進程通訊雙方就變成了Client和ServiceManager,而後ServiceManager從Binder表取出Binder的引用返給Client,這樣的話若是有多個Client的話,屢次返回引用就好了,可是事實上引用的都是放在ServiceManager中的Service。

當Client通過Binder驅動跟Service通訊的時候,每每須要獲取到Service的某個對象object。這時候爲了安全考慮,Binder會把object的代理對象proxyobject返回,這個對象擁有如出一轍的方法,可是沒有具體能力,只負責接收參數傳給真正的object使用。

因此完整的Binder通訊過程是

binder模型

OK,跨進程通訊就講清楚了。接下來咱們講講插件化中的Binder。

Binder與插件化

首先,咱們先回顧一下Activity的啓動過程,Instrumentation調用了ActivityManagerNative,這個AMN是咱們的本地對象,而後AMN調用getDefault拿到了ActivityManagerProxy,這我的AMP就是AMS在本地的代理。至關於binder模型中的Client,而這個AMP繼承的是IActivityManager,擁有四大組件的全部須要AMS參與的方法。本地經過調用這個AMP方法來間接地調用AMS。這樣,咱們就調用到了AMS啓動了Activity。

那麼,AMS如何與Client進行通訊呢?如今咱們經過Launcher啓動了Activity,確定要告訴Launcher 「沒你什麼事了,你洗洗睡吧」。你們能夠看到,這裏雙方的角色就發生了改變,AMS須要去發消息,承擔Client的角色,而Launcher這時候做爲Service提供服務。而此次通訊一樣也是使用的Binder機制。AMS這邊保存了一個ApplicationThreadProxy對象,這個對象就是Launcher的ApplicationThread的代理。AMS經過ATP給App發消息,App經過ApplicationThread處理。

Activity啓動流程

以上,就是Binder機制在Android中的運用,咱們後面會經過hook這個過程實現插件化。

總結

看了這麼多,可能仍是不少朋友不懂Binder。我也是這樣,很長一段時間都不知道Binder到底指的是啥。後來我看到了這樣一種定義:

  • 從進程間通訊的角度看,Binder 是一種進程間通訊的機制;

  • 從 Server 進程的角度看,Binder 指的是 Server 中的 Binder 實體對象;

  • 從 Client 進程的角度看,Binder 指的是對 Binder 代理對象,是 Binder 實體對象的一個遠程代理

  • 從傳輸過程的角度看,Binder 是一個能夠跨進程傳輸的對象;Binder 驅動會對這個跨越進程邊界的對象對一點點特殊處理,自動完成代理對象和本地對象之間的轉換。

二. ClassLoader

雙親委託模型

Java中默認有三種ClassLoader。分別是:

  • BootStrap ClassLoader:啓動類加載器,最頂層的加載器。主要負責加載JDK中的核心類。在JVM啓動後也隨着啓動,並構造Ext ClassLoader和App ClassLoader。
  • Extension ClassLoader:擴展類加載器,負責加載Java的擴展類庫。
  • App ClassLoader:系統類加載器,負責加載應用程序的全部jar和class文件。
  • 自定義ClassLoader:須要繼承自ClassLoader類。

ClassLoader默認使用雙親委託模型來搜索類。每一個ClassLoader都有一個父類的引用。當ClassLoader須要加載某個類時,先判斷是否加載過,若是加載過就返回Class對象。不然交給他的父類去加載,繼續判斷是否加載過。這樣 層層判斷,就到了最頂層的BootStrap ClassLoader來試圖加載。若是連最頂層的Bootstrap ClassLoader都沒加載過,那就加載。若是加載失敗,就轉交給子ClassLoader,層層加載,直到最底層。若是還不能加載的話那就只能拋出異常了。

經過這種雙親委託模型,好處是:

  • 更高效,父類加載一次就能夠避免了子類屢次重複加載
  • 更安全,避免了外界僞造java核心類。

Android中的ClassLoader

android從5.0開始使用art虛擬機,這種虛擬機在程序運行時也須要ClassLoader將類加載到內存中,可是與java不一樣的是,java虛擬機經過讀取class字節碼來加載,可是art則是經過dex字節碼來加載。這是一種優化,能夠合併多個class文件爲一個classes.dex文件。

android一共有三種類加載器:

  • BootClassLoader:父類構造器

  • PathClassLoader:通常是加載指定路徑/data/app中的apk,也就是安裝到手機中的apk。因此通常做爲默認的加載器。

  • DexClassLoader:從包含classes.dex的jar或者apk中,加載類的加載器,可用於動態加載。

看PathClassLoader和DexClassLoader源碼,都是繼承自BaseDexClassLoader。

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
    super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
}


public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);  //見下文
    }
}

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(parent);  //見下文
        //收集dex文件和Native動態庫【見小節3.2】
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
}
複製代碼

咱們能夠看到PathClassLoader的兩個參數都爲null,代表只能接受固定的dex文件,而這個文件是隻能在安裝後出現的。而DexClassLoader中optimizedDirectory,和librarySearchPath都是能夠本身定義的,說明咱們能夠傳入一個jar或者apk包,保證解壓縮後是一個dex文件就能夠操做了。所以,咱們一般使用DexClassLoader來進行插件化和熱修復。

能夠看到,BaseDexClassLoader有一個至關重要的過程就是初始化DexPathList。初始化DexPathList的過程主要是收集dexElements和nativeLibraryPathElements。一個Classloader能夠包含多個dex文件,每一個dex文件被封裝到一個Element對象。這element對象在初始化和熱修復邏輯中是至關重要的。當查找某個類時,會遍歷dexElements,若是找到就返回,不然繼續遍歷。因此當多個dex中有相同的類,只會加載前面的dex中的類。下面是這段邏輯的具體實現

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if (dex != null) {
            //找到目標類,則直接返回
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
    }
    return null;
}
複製代碼

總結

咱們先是講解了Java中類加載的雙親委託機制,而後介紹了Android中的幾種ClassLoader,從源碼角度介紹了兩種ClassLoader加載機制的不一樣。之後的插件化實踐中,咱們會常常用到DexClassLoader。

參考資料

Android跨進程通訊:圖文詳解 Binder機制 原理

寫給 Android 應用工程師的 Binder 原理剖析

深刻理解Android中的ClassLoader

Android類加載器ClassLoader

我是Android笨鳥之旅,一個陪着你慢慢變強的公衆號,歡迎關注我一塊兒學習,一塊兒進步哈~

相關文章
相關標籤/搜索