美文网首页Android知识Android开发Android技术知识
插件化知识梳理(7) - 类的动态加载入门

插件化知识梳理(7) - 类的动态加载入门

作者: 泽毛 | 来源:发表于2017-06-09 23:18 被阅读286次

    相关阅读

    插件化知识梳理(1) - Small 框架之如何引入应用插件
    插件化知识梳理(2) - Small 框架之如何引入公共库插件
    插件化知识梳理(3) - Small 框架之宿主分身
    插件化知识梳理(4) - Small 框架之如何实现插件更新
    插件化知识梳理(5) - Small 框架之如何不将插件打包到宿主中
    插件化知识梳理(6) - Small 源码分析之 Hook 原理
    插件化知识梳理(7) - 类的动态加载入门
    插件化知识梳理(8) - 类的动态加载源码分析
    插件化知识梳理(9) - 资源的动态加载示例及源码分析
    插件化知识梳理(10) - Service 插件化实现及原理


    一、前言

    插件化知识梳理(6) - Small 源码分析之 Hook 原理 这一章的学习完成之后,下一步我们将进入插件化加载的精髓,动态加载类的学习,在此之前,我们需要先准备一些关于类加载的知识。

    Android当中,支持动态加载的两种方式为:DexClassLoaderPathClassLoader。这两者之间的区别为:

    • DexClassLoader
    • 可以加载jar、apk、dex
    • 支持从SD卡目录加载。
    • PathClassLoader
    • 在许多文章中都有提到,在Dalvik虚拟机上,只能加载已经安装到系统中的Apk文件,也就是/data/app目录下的apk文件。之所以有这个限制是因为PathClassLoader会去读取data/dalvik-cache目录下经过优化后的dex文件,如果文件不存在,那么就会报错。由于手边没有机器,所以没有版本验证。
    • 而在ART虚拟机上,通过源码当中的注释,可以发现是支持的。

    二、具体实例

    实例的工程目录结构为:


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

    其中,applibrary模块分别依赖于libraryinterfacelibrarylibraryinterfaceAndroid Library类型的Module,下面,我们开始讲解整个工程的构建过程。

    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任务:

    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文件,我们通过解压可以看出:


    那么我们就需要通过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 知识梳理系列:

    相关文章

      网友评论

        本文标题:插件化知识梳理(7) - 类的动态加载入门

        本文链接:https://www.haomeiwen.com/subject/rfgtqxtx.html