知識總結(jié)之 插件化基礎(chǔ)ClassLoader

安卓插件化技術(shù)已經(jīng)作為一個優(yōu)秀的合格研發(fā)必備要求,學(xué)習(xí)和掌握現(xiàn)有不同種類動態(tài)加載方案 是提升個人技術(shù)深度有效途徑。

插件化基礎(chǔ) ClassLoader

ClassLoader是什么酱鸭?

ClassLoader 是將java編譯后的字節(jié)碼加載到虛擬機(jī)內(nèi)存中的用到工具類宏娄。

Android平臺虛擬機(jī)Dalvik/ART可以運(yùn)行的字節(jié)碼為.dex文件毫玖,Java平臺JVM虛擬機(jī)可以被加載的字節(jié)碼為.class文件炼蹦。針對Android平臺羡宙,若我們用ClassLoader加載指定地方的.dex,并且可以擴(kuò)展ClassLoader實(shí)現(xiàn)定制加載器框弛,就可以實(shí)現(xiàn)動態(tài)加載代碼的目的辛辨。

dex文件也是由jar轉(zhuǎn)化而來捕捂,android提供了轉(zhuǎn)化命令:

dx --dex --output=target.dex origin.jar // target.dex就是我們要的了

ClassLoader實(shí)例

Java虛擬機(jī)中類加載器:

系統(tǒng)默認(rèn)三個主要的類加載器瑟枫,每個類負(fù)責(zé)加載特定位置的類:
BootStrap,ExtClassLoader,AppClassLoader

  • BootStrap 引導(dǎo)類加載器斗搞,用來加載java核心庫(jre/lib/rt.jar),并非繼承ClassLoader慷妙,C代碼實(shí)現(xiàn)僻焚。
  • ExtClassLoader 擴(kuò)展類加載器,用來加載java擴(kuò)展庫中的類(jre/lib/ext/*.jar)膝擂。
  • AppClassLoader 系統(tǒng)類加載器虑啤,根據(jù)類路徑(ClassPath)來加載java類,java應(yīng)用層類都是該類加載架馋。

用IntelliJ 創(chuàng)建java虛擬機(jī)代碼

public class Run {
    public static void main(String args[]){
        StringBuilder stringBuilder = new StringBuilder();
        ClassLoader loader = Run.class. getClassLoader();
        while (loader != null){
            stringBuilder.append(loader.toString());
            stringBuilder.append("\n##");
            loader = loader.getParent();
        }
        System.out.println("ClassLoader:"+stringBuilder);
    }
}

打印信息中狞山,可以看到加載器類的層級關(guān)系。

##ClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
##sun.misc.Launcher$ExtClassLoader@511d50c0

Process finished with exit code 0

安卓虛擬機(jī)Dalvik/ART類加載器

image.png

安卓平臺虛擬機(jī)的類加載器實(shí)例和java不同叉寂,但都是繼承自ClassLoader的萍启。

  • BootClassLoader
    和java虛擬機(jī)中不同的是BootClassLoader是ClassLoader內(nèi)部類,由java代碼實(shí)現(xiàn)而不是c++實(shí)現(xiàn),是Android平臺上所有ClassLoader的最終parent,這個內(nèi)部類是包內(nèi)可見,所以我們沒法使用。

  • URLClassLoader
    只能用于加載jar文件屏鳍,但是由于 dalvik 不能直接識別jar勘纯,所以在 Android 中無法使用這個加載器。

  • BaseDexClassLoader
    PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader,其中的主要邏輯都是在BaseDexClassLoader完成的钓瞭。這些源碼在java/dalvik/system中驳遵。

  • DexClassLoader
    DexClassLoader支持加載APK、DEX和JAR山涡,也可以從SD卡進(jìn)行加載堤结。動態(tài)加載主要用到了該類特性,來動態(tài)加載不同類型壓縮包里的或這直接dex文件代碼鸭丛。

  • PathClassLoader

public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
    
     private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }
}

由于構(gòu)造參數(shù)optimizedDirectory指定為null霍殴,會直接使用dex文件原有的路徑來創(chuàng)建DexFile對象。也就是說在dalvik虛假機(jī)上PathClassLoader無法加載外部的動態(tài)代碼系吩。

類加載過程分析

JVM中ClassLoader通過defineClass方法加載jar里面的Class来庭,而Android平臺中這個方法被棄用了。取而代之的是loadClass方法穿挨。

    @Deprecated
    protected final Class<?> defineClass(byte[] b, int off, int len)
        throws ClassFormatError{
        throw new UnsupportedOperationException("can't load this type of class file");
    }

分析loadClass

     protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                }
            }
            return c;
    }

loadClass代碼分析可以看出月弛,加載前會看緩存中是否已經(jīng)加載,沒有加載則委托為parent科盛,如果parent沒有加載到帽衙,這child加載器開始findClass加載。這種加載方式被稱為雙親代理模型加載贞绵。

特點(diǎn):如果一個類被位于樹根的ClassLoader加載過厉萝,那么在以后整個系統(tǒng)的生命周期內(nèi),這個類永遠(yuǎn)不會被重新加載。共享功能谴垫,一些Framework層級的類一旦被頂層的ClassLoader加載過就緩存在內(nèi)存里面章母,以后任何地方用到都不需要重新加載。

除此之外還有隔離功能翩剪,不同繼承路線上的ClassLoader加載的類肯定不是同一個類乳怎,這樣的限制避免了用戶自己的代碼冒充核心類庫的類訪問核心類庫包可見成員的情況。

image.png

層級關(guān)系中父類沒有加載到時前弯,子類開始findClass

@Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = pathList.findClass(name);
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }
    
    public Class findClass(String name) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;
            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        return null;
    }
    
    public Class loadClassBinaryName(String name, ClassLoader loader) {
        return defineClass(name, loader, mCookie);
    }
    
    private native static Class defineClass(String name, ClassLoader loader, int cookie);

可以看出蚪缀,BaseDexClassLoader中有個pathList對象,pathList中包含一個DexFile的數(shù)組dexElements,熱修復(fù)原理也是修改該數(shù)組加載順序來實(shí)現(xiàn)恕出,dexPath傳入的原始dex(.apk,.zip,.jar等)文件在optimizedDirectory文件夾中生成相應(yīng)的優(yōu)化后的odex文件询枚,dexElements數(shù)組就是這些odex文件的集合,如果不分包一般這個數(shù)組只有一個Element元素浙巫,也就只有一個DexFile文件哩盲,而對于類加載呢,就是遍歷這個集合狈醉,通過DexFile去尋找廉油。最終調(diào)用native方法的defineClass。

擴(kuò)展ClassLoader實(shí)現(xiàn)定制化

可以重載loadClass方法并改寫類的加載邏輯苗傅,我們可以通過重寫loadClass方法避開雙親代理的框架抒线,這樣一來就可以在重新加載已經(jīng)加載過的類,也可以在加載類的時候注入一些代碼渣慕。

安卓平臺特有組件(activity嘶炭,service等)的加載是否可以直接加載呢?答案肯定是否的逊桦,這些組件都是需要在清單文件中注冊眨猎,然后才會被系統(tǒng)反射加載,并基于回調(diào)給予組件生命周期强经。

那我們想要加載一個安卓特有組件時睡陪,需要解決生命周期管理問題,才能正確的被系統(tǒng)調(diào)起來匿情。

——————
歡迎轉(zhuǎn)載兰迫,請標(biāo)明出處:常興E站 www.canking.win

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市炬称,隨后出現(xiàn)的幾起案子汁果,更是在濱河造成了極大的恐慌,老刑警劉巖玲躯,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件据德,死亡現(xiàn)場離奇詭異鳄乏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棘利,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門橱野,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赡译,你說我怎么就攤上這事仲吏〔幻” “怎么了蝌焚?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長誓斥。 經(jīng)常有香客問我只洒,道長,這世上最難降的妖魔是什么劳坑? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任毕谴,我火速辦了婚禮,結(jié)果婚禮上距芬,老公的妹妹穿的比我還像新娘涝开。我一直安慰自己,他們只是感情好框仔,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布舀武。 她就那樣靜靜地躺著,像睡著了一般离斩。 火紅的嫁衣襯著肌膚如雪银舱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天跛梗,我揣著相機(jī)與錄音寻馏,去河邊找鬼。 笑死核偿,一個胖子當(dāng)著我的面吹牛诚欠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漾岳,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼聂薪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝗羊?” 一聲冷哼從身側(cè)響起藏澳,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耀找,沒想到半個月后翔悠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體业崖,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年蓄愁,在試婚紗的時候發(fā)現(xiàn)自己被綠了双炕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡撮抓,死狀恐怖妇斤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丹拯,我是刑警寧澤站超,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站乖酬,受9級特大地震影響死相,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咬像,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一算撮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧县昂,春花似錦肮柜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狸驳,卻和暖如春预明,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耙箍。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工撰糠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辩昆。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓阅酪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汁针。 傳聞我的和親對象是個殘疾皇子术辐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

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