插件化知識(shí)梳理(7) - 類(lèi)的動(dòng)態(tài)加載入門(mén)


相關(guān)閱讀

插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件
插件化知識(shí)梳理(2) - Small 框架之如何引入公共庫(kù)插件
插件化知識(shí)梳理(3) - Small 框架之宿主分身
插件化知識(shí)梳理(4) - Small 框架之如何實(shí)現(xiàn)插件更新
插件化知識(shí)梳理(5) - Small 框架之如何不將插件打包到宿主中
插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理
插件化知識(shí)梳理(7) - 類(lèi)的動(dòng)態(tài)加載入門(mén)
插件化知識(shí)梳理(8) - 類(lèi)的動(dòng)態(tài)加載源碼分析
插件化知識(shí)梳理(9) - 資源的動(dòng)態(tài)加載示例及源碼分析
插件化知識(shí)梳理(10) - Service 插件化實(shí)現(xiàn)及原理


一、前言

插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理 這一章的學(xué)習(xí)完成之后,下一步我們將進(jìn)入插件化加載的精髓,動(dòng)態(tài)加載類(lèi)的學(xué)習(xí),在此之前,我們需要先準(zhǔn)備一些關(guān)于類(lèi)加載的知識(shí)。

Android當(dāng)中,支持動(dòng)態(tài)加載的兩種方式為:DexClassLoaderPathClassLoader。這兩者之間的區(qū)別為:

  • DexClassLoader
    • 可以加載jar、apk、dex
    • 支持從SD卡目錄加載。
  • PathClassLoader
    • 在許多文章中都有提到,在Dalvik虛擬機(jī)上,只能加載已經(jīng)安裝到系統(tǒng)中的Apk文件,也就是/data/app目錄下的apk文件。之所以有這個(gè)限制是因?yàn)?code>PathClassLoader會(huì)去讀取data/dalvik-cache目錄下經(jīng)過(guò)優(yōu)化后的dex文件,如果文件不存在,那么就會(huì)報(bào)錯(cuò)。由于手邊沒(méi)有機(jī)器,所以沒(méi)有版本驗(yàn)證。
    • 而在ART虛擬機(jī)上,通過(guò)源碼當(dāng)中的注釋?zhuān)梢园l(fā)現(xiàn)是支持的。

二、具體實(shí)例

實(shí)例的工程目錄結(jié)構(gòu)為:


  • app:宿主模塊
  • library:插件模塊
  • libraryinterface:插件接口模塊

其中,applibrary模塊分別依賴(lài)于libraryinterface,librarylibraryinterfaceAndroid Library類(lèi)型的Module,下面,我們開(kāi)始講解整個(gè)工程的構(gòu)建過(guò)程。

2.1 接口模塊 libraryinterface

接口模塊相當(dāng)于是宿主模塊和插件模塊所定義的一套標(biāo)準(zhǔn),宿主模塊遵循固定的業(yè)務(wù)邏輯,而具體的實(shí)現(xiàn)則根據(jù)插件模塊的不同而不同。
在接口模塊中,我們定義一個(gè)簡(jiǎn)單的接口IPlugin.java

public interface IPlugin {
    public int getVersion();
}

2.2 插件模塊 library

首先,我們?cè)诓寮K的build.gradle文件中,引入libraryinterface模塊

dependencies {
    //引入接口模塊
    compile project (':libraryinterface')
}

接著,我們編寫(xiě)一個(gè)實(shí)現(xiàn)類(lèi):

public class PluginImpl implements IPlugin {

    @Override
    public int getVersion() {
        return 1;
    }
}

接下來(lái)需要做的就是將該插件模塊打包成一個(gè)jar文件,同樣是在build.gradle文件中,創(chuàng)建一個(gè)Task任務(wù):

task makeJar(type: Copy) {
    delete 'build/libs/plugin.jar'
    from ('build/intermediates/bundles/release/')
    into ('../file/')
    include ('classes.jar')
    rename ('classes.jar','plugin.jar')
}

makeJar.dependsOn(build)

首先點(diǎn)擊make module


接下來(lái),在項(xiàng)目的根目錄下執(zhí)行命令:

./gradlew makeJar

就會(huì)得到一個(gè)plugin.jar文件,但是這個(gè)jar文件是不能夠被動(dòng)態(tài)加載的,因?yàn)樗鼉?nèi)部其實(shí)是.class文件,我們通過(guò)解壓可以看出:


那么我們就需要通過(guò)Android SDK自帶的dx工具進(jìn)行轉(zhuǎn)換,把它轉(zhuǎn)換為.dex,轉(zhuǎn)換后的文件為plugin_dex.jar

/Users/lizejun/Library/Android/sdk/build-tools/25.0.3/dx --dex --output=file/plugin_dex.jar file/plugin.jar

plugin_dex.jar解壓之后,可以看到它已經(jīng)被轉(zhuǎn)換成了.dex文件:


最后,將該jarpush到手機(jī)中的/sdcard/Plugin目錄下:

2.3 宿主模塊 app

首先,宿主模塊同樣需要依賴(lài)于接口模塊libraryinterface

dependencies {
    //引入接口模塊
    compile project (':libraryinterface')
}

在代碼當(dāng)中,我們通過(guò)DexClassLoader/PathClassLoader動(dòng)態(tài)外部的插件plugin_dex.jar,通過(guò)反射實(shí)例化PluginImpl類(lèi),并調(diào)用它的getVersion()方法進(jìn)行驗(yàn)證:

public class MainActivity extends AppCompatActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv_plug_result);
        getPluginA();
    }

    private void getPluginA() {
        File dexOutputDir = getDir("dex1", 0);
        String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
        DexClassLoader loader = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());
        try {
            Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
            IPlugin impl = (IPlugin) clz.newInstance();
            int version = impl.getVersion();
            mTextView.setText("Version=" + version);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void getPluginB() {
        String dexPath = Environment.getExternalStorageDirectory().toString() + "/Plugin/plugin_dex.jar";
        PathClassLoader loader = new PathClassLoader(dexPath, getClassLoader());
        try {
            Class clz = loader.loadClass("com.demo.lizejun.library.PluginImpl");
            IPlugin impl = (IPlugin) clz.newInstance();
            int version = impl.getVersion();
            mTextView.setText("Version=" + version);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

最終的結(jié)果為:



更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容