美文网首页
Anroid插件化一:jar包动态加载

Anroid插件化一:jar包动态加载

作者: DON_1007 | 来源:发表于2019-09-28 14:53 被阅读0次

Android中常用的两种类加载器:PathClassLoaderDexClassLoader,它们都继承于BaseDexClassLoader
PathClassLoader 可以加载已经安装到Android系统中的apk文件,在ART虚拟机上可以加载未安装的apkdex,在Dalvik则不行;DexClassLoader可以加载jardex和未安装的apk

考虑到需要兼容4.0版本的Android系统,以下选用DexClassLoader实现动态加载。

动态加载jar

一、新建项目PluginDemo

此时会默认生成Project的主module app
gradle版本选择使用3.1.4
二、新建module PluginJarDemo
新建类PluginJarDemo,以作演示。

package com.don.pluginjardemo;

import android.util.Log;

/**
 * Created by don on 2019-09-09
 */
public class PluginJarDemo {
    private final String TAG = "PluginJarDemo";
    private String content;

    public void writeTest(String content) {
        Log.i(TAG, "writeTest " + content + "  " + this);
        this.content = content;
    }

    public String readTest() {
        Log.i(TAG, "readTest " + this);
        return content;
    }

}

接下来通过DexClassLoader动态加载PluginJarDemo

1、编译module PluginJarDemo,生成jar

task makeJar(type: Jar, dependsOn: [':PluginJarDemo:assembleRelease' ]) {

    archiveName = "pluginDemo.jar"
    destinationDir = file('build/libs')

    def where = "build/intermediates/classes/release"
    from(where)
    from fileTree(dir: 'src/main', includes: ['assets/**'])

    exclude('**/R.class')
    exclude('**/R\$*.class')
    include "**/*.*"
}

在命令行中执行
gradlew :PluginJarDemo:makeJar
PluginJarDemobuild/libs/目录下会生成pluginDemo.jar文件。
这种直接生成的jar包是不能被DexClassLoader加载的,因为其中没有dex文件。

2、生成包含dex文件的新jar

task makeDex(type: Exec, description: 'for PluginJarDemo dex') {
    doFirst {
        println("convert jar to dex start")

        // 拼接的dex转换工具路径
        Properties properties = new Properties()
        //local.properites也放在posdevice目录下
        File propertyFile = new File(rootDir.getAbsolutePath() + "/local.properties")
        properties.load(propertyFile.newDataInputStream())
        def dexToolPath = properties.getProperty('sdk.dir') + "/build-tools/27.0.3"
        // dex转换工具参数
        def dexToolArgs = "--dex --output="
        def releaseJar = "build/libs/pluginDemo.jar"
        def dexJar = "build/libs/pluginDex.jar"

        def finalCmd
        def dxToolName
        if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
            // Windows
            dxToolName = "dx.bat"
            finalCmd = dexToolPath + "/$dxToolName " + dexToolArgs + dexJar + " " + releaseJar
            finalCmd = finalCmd.replaceAll("/", "\\\\")
            commandLine "cmd", "/c", finalCmd
        } else {
            // linux
            dxToolName = "dx"
            finalCmd = dexToolPath + "/$dxToolName " + dexToolArgs + dexJar + " " + releaseJar
            finalCmd = finalCmd.replaceAll("\\\\", "/")
            commandLine "/bin/sh", "-c", finalCmd
        }
        println("convert jar to dex end, command: " + finalCmd)
    }
}

在命令行中运行
gradlew :PluginJarDemo:makeDex
PluginJarDemobuild/libs/目录下会生成pluginDex.jar文件,这个文件包含class.dex文件,可被DexClassLoader动态加载。

三、动态加载pluginDex.jar

在主module app中新建类JarLoad用于动态加载pluginDex.jar

package com.don.plugindemo;

import android.content.Context;
import android.util.Log;

import java.io.File;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

/**
 * Created by don on 2019-09-09
 */
public class JarLoader {
    private final static String TAG = "JarLoader";
    private final String DEMO_CLASS = "com.don.pluginjardemo.PluginJarDemo";
    private Class mDemoClass;
    private Object mDemoInstance;

    public void load(Context context, String pluginPath) {
        File file = new File(pluginPath);
        if (!file.exists()) {
            Log.i(TAG, "load ignore, invalid file: " + pluginPath);
            return;
        }
        String dataFile = context.getFilesDir().getAbsolutePath();
        try {
            DexClassLoader dexClassLoader = new DexClassLoader(pluginPath, dataFile, null, getClass().getClassLoader());
            mDemoClass = dexClassLoader.loadClass(DEMO_CLASS);
            mDemoInstance = mDemoClass.newInstance();
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

    public void writeTest(String content) {
        try {
            Method method = mDemoClass.getDeclaredMethod("writeTest", String.class);
            method.invoke(mDemoInstance, content);
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

    public String readTest() {
        try {
            Method method = mDemoClass.getDeclaredMethod("readTest");
            return method.invoke(mDemoInstance).toString();
        } catch (Exception e) {
            Log.w(TAG, e);
        }
        return null;
    }

}

DexClassLoader的创建需要填4个参数:

  • 第一个是jar包的地址
  • 第二个是jar包中dex文件被加载之后的路径,需要可执行,这里使用了app的安* 装根目录
  • 第三个是so文件目录,如果该jar包需要加载so,则需要传入so文件所在目录,同样的,此目录需要有可执行权限
  • 第四个是父类构造器,使用当前app的构造器即可

jar包动态加载成功之后可以通过反射机制创建jar包中的PluginJarDemo对象并操作。

在主界面添加一个按钮,点击之后读取PluginJarDemo实例中的content变量。

package com.don.plugindemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private JarLoader mJarLoader;
    private Button mJarBtn;
    private TextView mResultTxt;
    private View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.jarBtn:
                    mJarLoader.writeTest("jar load test");
                    mResultTxt.setText(mJarLoader.readTest());
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        mJarLoader = new JarLoader();
        mJarLoader.load(this,"sdcard/pluginDex.jar");
    }

    private void initView() {
        mJarBtn = (Button) findViewById(R.id.jarBtn);
        mJarBtn.setOnClickListener(mClickListener);
        mResultTxt = (TextView) findViewById(R.id.resultTxt);
    }
}

运行,点击按钮 JAR
如下图,可以从pluginDex.jar中成功读取content变量

image.png

动态加载dex与动态加载jar类似,唯一的区别是需要将打包的jar转换为dex文件,以上述实现为例,通过下面的命令可以得到一个可用于动态加载的dex文件
dx --dex --output=pluginDex.dex pluginDemo.jar
接下来通过加载pluginDex.dex的实现,与动态加载pluginDex.jar文件一样。

相关文章

  • Anroid插件化一:jar包动态加载

    Android中常用的两种类加载器:PathClassLoader和DexClassLoader,它们都继承于Ba...

  • JVM之类加载机制

    Android类加载器 理解类加载 Eclipse使用第三方的插件其实就是动态加载Jar包里的Class字节码进行...

  • Android类加载器

    JVM之类加载机制 理解类加载 Eclipse使用第三方的插件其实就是动态加载Jar包里的Class字节码进行工作...

  • android jar包文件只编译不打包到apk中全流程

    需求:动态加载apk插件化开发时,插件工程中依赖的dl-libs.jar包,只想它参与编译,而不用打包到apk中 ...

  • Android插件化(二)动态加载Activity 和生命周期

    我们在上篇文章中主要讲了加载jar包和加载apk换肤,没看的可以直接戳着个链接: 1.Android插件化(一) ...

  • 动态加载jar包

    概述 前段时间在开发外设驱动程序时,涉及到了动态加载jar包的知识,于是开始学习了下。这里主要是完成客户端驱动ja...

  • Android 插件化、热修复、增量更新介绍

    一、插件化 概述:Android插件化技术,可以实现功能模块的按需加载和动态更新,其本质是动态加载未安装的apk。...

  • 插件化

    插件化插件化和热修复都 属于动态加载技术 动态加载技术在应用程序运行时,动态加载一些程序中原本不存在的可执行文件并...

  • android插件化

    一.插件化来由 65536/64k 二.插件化所要解决的问题 1.动态加载apk 2.资源加载 3.代码加载

  • Android[系统学习总结]

    1 Android[插件化或称动态加载] Android插件化完全解析 http://www.androidb...

网友评论

      本文标题:Anroid插件化一:jar包动态加载

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