Android 動態(tài)加載技術(shù)

動態(tài)加載技術(shù)汉规,也叫插件化技術(shù)哭尝,在技術(shù)驅(qū)動型公司中扮演著相當(dāng)重要的角色,當(dāng)項(xiàng)目越來越龐大的時候依疼,需要通過插件化來減輕應(yīng)用的內(nèi)存和CPU占用痰腮,還可以實(shí)現(xiàn)熱插拔,即不在發(fā)布新版本的情況下更新某些模塊律罢。動態(tài)加載時一項(xiàng)很復(fù)雜的技術(shù)膀值。這主要介紹其中的三個基礎(chǔ)性問題。

資源訪問

問題描述

宿主程序調(diào)起未安裝的插件apk沧踏,一個很大的問題就是資源如何訪問,具體來說就是插件中凡是以R開頭的資源都不能訪問了巾钉。這是因?yàn)樗拗鞒绦蛑胁]有插件的資源翘狱,所以通過R來加載插件的資源時行不通的,程序會拋出異常:無法找到某某id 對應(yīng)的資源砰苍。

解決思路分析

  1. 將插件中的資源在宿主程序中也預(yù)置一份潦匈,這雖然能解決問題阱高,但是就會產(chǎn)生一些弊端:

    • 首先,這樣就需要宿主和插件同時持有一份相同的資源茬缩,增加了宿主apk的大谐嗑;
    • 其次凰锡,在這種模式下未舟,每次發(fā)布一個插件都需要將資源復(fù)制到宿主程序中,這意味著沒發(fā)布一個插件都要更新一下宿主程序掂为,這就和插件化的思想相違背了裕膀。
    • 因?yàn)椋寮哪康木褪且獪p小宿主程序apk包的大小菩掏,同時降低宿主程序的更新頻率并做到自由裝載模塊魂角。所以這一思路不可取,它限制了插件化的線上更新這一重要特性智绸。
  2. 首先將插件中的資源解壓出來野揪,然后通過文件流去讀取資源。

    這樣做理論上可行的瞧栗,但是實(shí)際操作起來還是有很大的困難斯稳。

    • 首先,不同資源有不用的文件流格式迹恐,比如圖片挣惰、XML等;
    • 其次殴边,針對不同設(shè)備加載的資源可能是不一樣的的憎茂,如何選擇合適的資源也是一個需要解決的問題。

    基于這兩點(diǎn)這種方法也是不可取的锤岸,因?yàn)閷?shí)現(xiàn)起來有較大的難度竖幔。

  3. 參考模仿系統(tǒng)加載訪問資源的方式

    Activity的工作主要是通過 ContextImpl 來完成,Activity中有一個叫 mBase 的成員變量是偷,它的類型就是 ContextImpl拳氢。注意到Context中有兩個抽象方法,看起來是和資源有關(guān)的蛋铆,實(shí)際上Context 就是通過它們來獲取資源的馋评。這兩個抽象方法的真正實(shí)現(xiàn)是在 ContextImpl中,也就是說刺啦,只要實(shí)現(xiàn)了這兩個方法留特,就可以解決資源問題了。

    public abstract AssetManager getAssets();
    public abstract Resources getResources();
    

代碼實(shí)現(xiàn)

    protected fun loadResources() {
        try {
            val assetManager = AssetManager::class.java.newInstance()
            val addAssetPath = assetManager.javaClass.getMethod("addAssetPath", String::class.java)
            addAssetPath.invoke(assetManager, mDexPath)
            mAssetManager = assetManager
        } catch (e: Exception) {
            e.printStackTrace()
        }

        val superRes = super.getResources()
        val mResources = Resources(mAssetManager, superRes.displayMetrics, superRes.configuration)
        val mTheme = mResources.newTheme()
        mTheme.setTo(super.getTheme())
    }

從 loadResources() 的實(shí)現(xiàn)可以看出,加載資源的方法是通過反射磕秤,通過調(diào)用 AssetManager 中的 addAssetPath 方法乳乌,我們可以將一個 apk 中的資源加載到Resources 對象中,由于 addAssetPath 是隱藏 API 我們無法直接調(diào)用市咆,所以只能通過反射汉操。下面是它的聲明,通過注釋我們可以看出蒙兰,傳遞的路徑可以是zip 文件也可以是一個資源目錄磷瘤,而 apk 通過AssetManager 來創(chuàng)建一個新的Resources對象,通過這個對象我們就可以訪問插件apk中的資源了搜变,這樣一來問題就解決了采缚。

    /**
     * Add an additional set of assets to the asset manager.  This can be
     * either a directory or ZIP file.  Not for use by applications.  Returns
     * the cookie of the added asset, or 0 on failure.
     * {@hide}
     */
    public final int addAssetPath(String path) {
        return  addAssetPathInternal(path, false);
    }

然后在代理Activity 中實(shí)現(xiàn) getAssets() 和 getResources()

    override fun getResources(): Resources {
        return if (mResources == null) super.getResources() else mResources!!
    }

    override fun getAssets(): AssetManager {
        return if (mAssetManager == null) super.getAssets() else mAssetManager!!
    }

Activity生命周期的管理

  • 反射方式
  • 接口方式

反射方式

    override fun onResume() {
        super.onResume()
        val onResume: Method? = mActivityLifecircleMethods.get("onResume")
        try {
            onResume?.invoke(mRemoteActivity, arrayOf(Any()))
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

缺點(diǎn):

  • 反射代碼寫起來比較復(fù)雜
  • 過多使用反射會影響性能

接口方式

interface DLPlugin{
    fun onCreate()
    fun onStart()
    fun onResume
    ...
}

在代理Activity中調(diào)用即可

    override fun onResume() {
        mRemoteActivity.onResume()
        super.onResume()
    }
    ...

插件 ClassLoader 的管理 page 468

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挠他,隨后出現(xiàn)的幾起案子扳抽,更是在濱河造成了極大的恐慌,老刑警劉巖殖侵,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贸呢,死亡現(xiàn)場離奇詭異,居然都是意外死亡拢军,警方通過查閱死者的電腦和手機(jī)楞陷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茉唉,“玉大人固蛾,你說我怎么就攤上這事《嚷剑” “怎么了艾凯?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長懂傀。 經(jīng)常有香客問我览芳,道長,這世上最難降的妖魔是什么鸿竖? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮铸敏,結(jié)果婚禮上缚忧,老公的妹妹穿的比我還像新娘。我一直安慰自己杈笔,他們只是感情好闪水,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒙具,像睡著了一般球榆。 火紅的嫁衣襯著肌膚如雪朽肥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天持钉,我揣著相機(jī)與錄音衡招,去河邊找鬼。 笑死每强,一個胖子當(dāng)著我的面吹牛始腾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播空执,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼浪箭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辨绊?” 一聲冷哼從身側(cè)響起奶栖,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎门坷,沒想到半個月后宣鄙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拜鹤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年框冀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敏簿。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡明也,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惯裕,到底是詐尸還是另有隱情温数,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布蜻势,位于F島的核電站撑刺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏握玛。R本人自食惡果不足惜够傍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挠铲。 院中可真熱鬧冕屯,春花似錦、人聲如沸拂苹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浴韭,卻和暖如春丘喻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背念颈。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工泉粉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舍肠。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓搀继,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翠语。 傳聞我的和親對象是個殘疾皇子叽躯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容