Android dex动态加载(Kotlin版)

这篇具有很好参考价值的文章主要介绍了Android dex动态加载(Kotlin版)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

环境
  1. 语言–Kotlin
  2. JDK11
  3. SDK33
  4. AndroidStudio版本
Android Studio Dolphin | 2021.3.1 Patch 1
Build #AI-213.7172.25.2113.9123335, built on September 30, 2022
概述
  1. libaray项目打包成jar
  2. jar通过dx/d8命令行工具转为dex.jar
  3. dex.jar放到assets目录下
  4. App启动读取assets中的dex.jar复制到App可访问的文件夹中(建议内部存储的沙盒中,不受权限限制)
  5. 实例化DexClassLoader加载dex获取ClassLoader对象
  6. 通过ClassLoader.loadClass方法,获取想要执行的类
  7. 可以通过接口实现or反射实现,获取类的内部属性或者执行类的内部方法
ClassLoader

与JVM不同,Dalvik的虚拟机不能用ClassCload直接加载.dex,Android从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader;
而这两个类就是我们加载dex文件的关键,这两者的区别是:

1.DexClassLoader:可以加载jar/apk/dex,可以从SD卡中加载未安装的apk;
2.PathClassLoader:要传入系统中apk的存放Path,所以只能加载已经安装的apk文件。

因此我们动态加载dex的核心类是DexClassLoader

实践

打包jar
1.创建一个library项目,命名为dexlib
2.添加如下代码:

引入Glide是为了实验第三方库依赖的可行性。

    //Glide4.x
    implementation 'com.github.bumptech.glide:glide:4.13.1'
    implementation 'com.github.bumptech.glide:okhttp3-integration:4.13.1'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.13.1'
class DexWork {

    fun showNavToast(context: Context, text: String) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
    }


    fun loadImage(imageView: ImageView, url: String) {
        Glide.with(imageView.context).load(url).into(imageView)
    }


    fun getClassName(): String {
        return this.javaClass.canonicalName
    }
}
3.build.gradle中添加打包jar代码,并sync now
//根据Library名称生存jar包到build目录下
//可根据自己需求更改
task againMakeJar(type: Copy) {
    //Library名称
    def name = project.name
    //删除之前的旧jar包
    delete 'libs/' + name + '.jar'
    //从这个目录下取出默认jar包,不同版本目录均不一样,根据自己项目在build中找classes.jar所在目录
    from('build/intermediates/compile_library_classes_jar/release/')
    into('libs/') //将jar包输出到指定目录下
    include('classes.jar')
    rename('classes.jar', name + '.jar') //自定义jar包的名字
}
againMakeJar.dependsOn(build)
4.gradle中双击againMakeJar,在dexlib/libs中生成dexlib.jar
生成dex.jar
1.在dexlib/libs下打开命令行
2.参考Android 使用dx/d8将jar转换为dex,运行对应的命令行
3.等几秒生成dexlib_dex.jar
4.dexlib_dex.jar是一个包含dex等jar
app项目
1.dexlib_dex.jar放到app项目等assets目录下,并在build.gradle中添加Glide依赖。
2.启动执行如下代码,将dexlib_dex.jar复制到内部存储到沙盒中去。
private val dexName = "dexlib_dex.jar"
Utils.copyDex(this, dexName)
    /**
     * 复制dex到沙盒中
     */
    fun copyDex(context: Context, dexName: String) {
        val cacheFile = File(context.filesDir, "dex")
        if (!cacheFile.exists()) {
            cacheFile.mkdirs()
        }
        val internalPath = cacheFile.absolutePath + File.separator + dexName
        val desFile = File(internalPath)
        if (!desFile.exists()) {
            desFile.createNewFile()
        }
        var `in`: InputStream? = null
        var out: OutputStream? = null
        try {
            `in` = context.applicationContext.assets.open(dexName)
            out = FileOutputStream(desFile.absolutePath)
            val bytes = ByteArray(1024)
            var len = 0
            while (`in`.read(bytes).also { len = it } != -1) out.write(bytes, 0, len)
            out.flush()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                `in`?.close()
                out?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
3.执行如下方法,加载dex,获取到DexClassLoader对象。
Utils.loader = Utils.loadDexClass(this, dexName)
    /**
     * 加载dex
     */
    fun loadDexClass(context: Context, dexName: String): DexClassLoader? {
        try {
            //下面开始加载dex class
            //1.待加载的dex文件路径,如果是外存路径,一定要加上读外存文件的权限,
            //2.解压后的dex存放位置,此位置一定要是可读写且仅该应用可读写
            //3.指向包含本地库(so)的文件夹路径,可以设为null
            //4.父级类加载器,一般可以通过Context.getClassLoader获取到,也可以通过ClassLoader.getSystemClassLoader()取到。
            val cacheFile = File(context.filesDir, "dex")
            val internalPath = cacheFile.absolutePath + File.separator + dexName
            return DexClassLoader(internalPath, cacheFile.absolutePath, null, context.classLoader)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }
4.根据完整类名获取DexWork类,反射代码执行DexWork类中的方法,完整Activity代码如下:

Tips:接口实现的方式相较于反射实现起来更简单,可参考:https://blog.csdn.net/wy353208214/article/details/50859422

class NormalActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_show)

        /**
         * 非静态类反射,kt代码跟java代码反射调用完全一致
         * invoke 第一个参数传入类实例
         */
        val cla = Utils.loader?.loadClass("com.demon.dexlib.DexWork")

        cla?.run {
            val className = getMethod("getClassName").invoke(newInstance()) as String
            findViewById<TextView>(R.id.text).text = className

            findViewById<Button>(R.id.btn1).setOnClickListener {
                getMethod("showNavToast", Context::class.java, String::class.java).invoke(newInstance(), this@NormalActivity, className)
            }

            val img = findViewById<ImageView>(R.id.iv)
            findViewById<Button>(R.id.btn2).setOnClickListener {
                getMethod("loadImage", ImageView::class.java, String::class.java).invoke(
                    newInstance(), img,
                    "https://idemon.oss-cn-guangzhou.aliyuncs.com/D.png"
                )
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="原生toast"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />



    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Image"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn1" />



    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Hello World!"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn2" />


    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/text" />
</androidx.constraintlayout.widget.ConstraintLayout>
5.运行app项目,发行可以正常DexWork中的代码,图片也可以正常加载(注意网络权限)。
6.dex动态加载实践完成。也可知:app项目同时引入library中的第三库依赖,可以解决jar无法打包第三方库代码的问题。

效果截图

Android dex动态加载(Kotlin版)

源码

https://github.com/DeMonDemoSpace/DexDynamicLoad

参考

https://blog.csdn.net/wy353208214/article/details/50859422文章来源地址https://www.toymoban.com/news/detail-411382.html

到了这里,关于Android dex动态加载(Kotlin版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android拖放startDragAndDrop拖拽onDrawShadow动态添加View,Kotlin(3)

    图像随着手指拖放滑动: Android View拖拽/拖放DragAndDrop自定义View.DragShadowBuilder,Kotlin(2)-CSDN博客 文章浏览阅读54次。Android DynamicGrid:拖曳交换位置Android DynamicGrid是一个第三方开源项目,DynamicGrid在github上的项目主页是:https://github.com/askerov/DynamicGrid它实现在一个网格布

    2024年02月08日
    浏览(43)
  • Android优化RecyclerView图片展示:Glide成堆加载批量Bitmap在RecyclerView成片绘制Canvas,Kotlin(b)

    对 Android GridLayoutManager Glide批量加载Bitmap绘制Canvas画在RecyclerView,Kotlin(a)-CSDN博客 改进,用Glide批量把Bitmap加载出来,然后在RecyclerView成片成堆的绘制Canvas,此种实现是RecyclerView加载多宫格图片展示,卡顿丢帧最低的一种实现,上下滑动流畅。 Android GridLayoutManager Glide批量

    2024年04月25日
    浏览(43)
  • Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,Kotlin

    build.gradle文件: 如果手机图片很多,假设已经将全部图片装入宫格的列表,在快速上下滑动过程中,由于glide会累积每一个图片的加载任务,如果图片比较大,上下滑动时间又很长,那么累积任务会很严重,导致异常发生,实现在RecyclerView切入后台(或不可见)时候,然后再

    2024年02月10日
    浏览(48)
  • VS Code环境下配置Kotlin语言开发环境

    目录 一、安装VS Code扩展 1.安装Kotlin Language 2. 安装、配置Code Runner 二、安装Kotlin-compiler 1.下载Kotlin-compiler 2.安装JDK 3.配置环境变量 三、测试代码 安装成功后进入Code Runner扩展设置,下滑勾选Code-runner:Run In Terminal  打开Kotlin官网  Kotlin https://kotlinlang.org/  Get started打开Kotlin的官

    2024年02月09日
    浏览(53)
  • Android java项目添加kotlin混合开发环境配置

    Android Studio java代码中添加kotlin混合开发 1.项目的build.gradle中添加kotlin-gradle-plugin buildscript {     repositories {         google()         jcenter()              }     dependencies {         classpath \\\'com.android.tools.build:gradle:7.3.1\\\'         classpath \\\"org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20\\\"

    2023年04月19日
    浏览(48)
  • Android开发:kotlin语言实现简易计算器

    输入两个数字,可选加减乘除操作符,并计算显示对应结果 随系统切换语言 可对结果进行四舍五入操作 界面布局:activity_main.xml文件代码 字符定义:string.xml文件代码 逻辑实现:MainActivity.kt 文件代码 方法一(偷懒): 复制文件到对应位置 方法二: 1. 绘制界面 2. 编写逻辑

    2023年04月08日
    浏览(48)
  • Android 安卓开发语言kotlin与Java该如何选择

            如今在Android开发中,应用层开发语言主要是Java和Kotlin,Kotlin是后来加入的,主导的语言还是Java。kotlin的加入仿佛让会kotlin语言的开发者更屌一些,其实不然。         有人说kotlin的引入是解决开发者复杂的逻辑,并且对空指针控制的比较友好,但是我们在开

    2024年02月11日
    浏览(66)
  • Android studio心得——fragment动态加载

    在Android应用程序中,Fragment是一种可以嵌入Activity中的组件。通过 Fragment,我们可以将UI 目录 前言 一、什么是Android Studio 二、简介Fragment 三、学期知识汇总 四、什么是碎片(Fragment) 五、页面实现步骤 1.程序APP主界面的常用例子 2.定义4个Fragment 3.activity_main.xml代码展示 4.四个

    2024年02月09日
    浏览(41)
  • Android 编译优化——dex2oat编译

    Android Runtime (ART) 包含一个具备代码分析功能的即时 (JIT) 编译器,该编译器可以在 Android 应用运行时持续提高其性能。JIT 编译器对 Android 运行组件当前的预先 (AOT) 编译器进行了补充,可以提升运行时性能,节省存储空间,加快应用和系统更新速度。相较于 AOT 编译器,JIT 编译

    2024年04月16日
    浏览(35)
  • android studio创建一个新的项目为什么默认是kotlin语言而选择不了java语言

    关于android studio语言选择的问题。 我在进入android studio为什么创建一个新项目之后选择不了java语言有什么办法可以解决。 解决办法:这个模式下选着一个Empty Activity模块就可以使用java语言。 这对于刚刚接触anaroid studio新手比较管用。  

    2024年02月11日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包