插件化知識梳理(7) 類的動態加載入門

1、前言

插件化知識梳理(6) - Small 源碼分析之 Hook 原理 這一章的學習完成以後,下一步咱們將進入插件化加載的精髓,動態加載類的學習,在此以前,咱們須要先準備一些關於類加載的知識。java

Android當中,支持動態加載的兩種方式爲:DexClassLoaderPathClassLoader。這二者之間的區別爲:bash

  • DexClassLoader
  • 能夠加載jar、apk、dex
  • 支持從SD卡目錄加載。
  • PathClassLoader
  • 在許多文章中都有提到,在Dalvik虛擬機上,只能加載已經安裝到系統中的Apk文件,也就是/data/app目錄下的apk文件。之因此有這個限制是由於PathClassLoader會去讀取data/dalvik-cache目錄下通過優化後的dex文件,若是文件不存在,那麼就會報錯。因爲手邊沒有機器,因此沒有版本驗證。
  • 而在ART虛擬機上,經過源碼當中的註釋,能夠發現是支持的。

2、具體實例

實例的工程目錄結構爲: app

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

其中,applibrary模塊分別依賴於libraryinterfacelibrarylibraryinterfaceAndroid Library類型的Module,下面,咱們開始講解整個工程的構建過程。ide

2.1 接口模塊 libraryinterface

接口模塊至關因而宿主模塊和插件模塊所定義的一套標準,宿主模塊遵循固定的業務邏輯,而具體的實現則根據插件模塊的不一樣而不一樣。 在接口模塊中,咱們定義一個簡單的接口IPlugin.java工具

public interface IPlugin {
    public int getVersion();
}
複製代碼

2.2 插件模塊 library

首先,咱們在插件模塊的build.gradle文件中,引入libraryinterface模塊源碼分析

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

接着,咱們編寫一個實現類:學習

public class PluginImpl implements IPlugin {

    @Override
    public int getVersion() {
        return 1;
    }
}
複製代碼

接下來須要作的就是將該插件模塊打包成一個jar文件,一樣是在build.gradle文件中,建立一個Task任務:gradle

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)
複製代碼

首先點擊make module優化

接下來,在項目的根目錄下執行命令:

./gradlew makeJar
複製代碼

就會獲得一個plugin.jar文件,可是這個jar文件是不可以被動態加載的,由於它內部實際上是.class文件,咱們經過解壓能夠看出: ui

那麼咱們就須要經過 Android SDK自帶的 dx工具進行轉換,把它轉換爲 .dex,轉換後的文件爲 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解壓以後,能夠看到它已經被轉換成了.dex文件:

最後,將該 jarpush到手機中的 /sdcard/Plugin目錄下:

2.3 宿主模塊 app

首先,宿主模塊一樣須要依賴於接口模塊libraryinterface

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

在代碼當中,咱們經過DexClassLoader/PathClassLoader動態外部的插件plugin_dex.jar,經過反射實例化PluginImpl類,並調用它的getVersion()方法進行驗證:

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();
        }
    }

}
複製代碼

最終的結果爲:


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索