花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密

本文之天下剑学,一学即通,助你突破剑学上限,自生天剑护墙气体!

(一)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(二)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(三)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(四)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(五) 大型项目架构:全动态插件化+模块化+Kotlin+协程+Flow+Retrofit+JetPack+MVVM+极限瘦身+极限启动优化+架构示例+全网唯一
(六) 大型项目架构:解析全动态插件化框架WXDynamicPlugin是如何做到全动态化的?
(七) 还在不断升级发版吗?从0到1带你看懂WXDynamicPlugin全动态插件化框架
(八) Compose插件化:一个Demo带你入门Compose,同时带你入门插件化开发

一、前言

1. 客户端应用配置文件数据,或者配置数据, 或者其他固定数据有哪些?

1.1 首页固定菜单栏数据

1.2 应用固定配置数据

1.3 固定省市区数据等

2. 这些怎么动态处理?

第一次启动就把数据下载下来,在本地存好,

以后每次下次直接使用,有新的数据版本再次从网络拿取,再次替换本地存好

3. 有哪些可以执行方案,如果首页就用到,在第一次启动中哪种方案速度最快?

本文实例工程将采用 性能优化到极致 的天剑境界方案进行介绍:

同时输出对比 耗时表格 以此来证明

4、这样做的好处有哪些?

一切都是为了用户体验,让体验做到极致

试想一下,如果首页用到什么配置数据,或者在启动优化过程中:即点开 Launcher 桌面图标到首页展示第一个界面之前, 用到某些配置数据,比如插件化模块,先加载哪些插件,加载本地插件哪些版本,新版本还是旧版,还是去网络下载等一系列的本地配置读取怎么做?

1、如果写死在代码里面没法动态修改

2、如果将配置文件写在 assets 下,读取慢,读取完还要解析 ,而且无法动态修改

3、如果将配置数据存放到 SharedPreferences 下,效果还不是很理想,偶尔还可能导致ANR

4、大厂推广的 MMKV 来存储,拿到存储后json,用 fastJson 号称最快 json 解析工具, 强强结合,但是 MMKV 首次要先初始化, fastJson 首次很慢,

5、最新一代存储KV工具 FastKV , 来存储 Protobuf 数据,但是 FastKV 还是要先初始化 再来存储,Protobuf 的首次解析一样的也很慢

6、还有什么办法没有? 我曾研究过,也曾问过10多个大厂高级工程师,然而好像到了上面第5就已经是他们的最高上限了

7、没有办法,那还得自己来实现,突破极限,本文下面示例工程将来介绍:

二、示例项目介绍研究

  1. 准备好示例需求json数据:我们假设数据为省市区数据如下:
  2. 为了数据量做到极致小,我们处理成如下格式数据:
  3. 我们在处理成一份 protobuf 数据来方便对比,这个没法截图看了
  4. 我们建好项目工程如下图:
  5. 其中 WX-Dex-ICityModelWX-Dex-ICityModel-PluginImpl 为lib工程
  6. WX-Dex-ICityModel 下面只是一个接口如下:
kotlin 复制代码
interface ICityModel {

    fun cityMap(): Map<String, List<String>>

}
  1. WX-Dex-ICityModel-PluginImpl 下面实现接口如下(数据太多,这里我删去大部分数据后贴出来):
kotlin 复制代码
class PluginCityModelImpl : ICityModel {

    override fun cityMap() = mapOf(
        "北京" to listOf(
            "东城区2", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县"
        ), "天津" to listOf(
            "和平区", "河东区", "河西区", "南开区", "河北区", "红挢区", "滨海新区", "东丽区", "西青区", "津南区", "北辰区", "宁河区", "武清区", "静海县", "宝坻区", "蓟县"
        ), "河北" to listOf(
            "石家庄", "唐山", "秦皇岛", "邯郸", "邢台", "保定", "张家口", "承德", "沧州", "廊坊", "衡水"
        ), "山西" to listOf(
            "太原", "大同", "阳泉", "长治", "晋城", "朔州", "晋中", "运城", "忻州", "临汾", "吕梁"
        )
}
  1. WX-Dex-ICityModel-PluginImpl lib工程处理成插件dex文件,具体怎么操作原理,怎么使用,请看我前面写的插件化文章: Compose插件化:一个Demo带你入门Compose,同时带你入门插件化开发
  2. 然后我们看下
    原始json数据(city.txt),
    处理后的json数据(city_out.txt
    处理后的Protobuf数据(city_out_p
    出来后的dex插件文件(city_model_lib_dex 可以看出 Protobuf数据是最小的,原始数据最大,但实际相差其实并不是很大
  3. 示例工程app截图如下:

项目地址 github
项目地址 gitee

三、研究分析

  1. 分析首次 fastkv 初始化就放在调用的类里面:
kotlin 复制代码
class CityModelRepository private constructor() {

    private val context by lazy { SampleApplication.application }
    private val kv by lazy { FastKV.Builder(context, "fastKV").build() }

    companion object {
        val instance by lazy { CityModelRepository() }
    }


    fun saveJson() {
        val json = JsonTestDataManager.getJsonByFile("city/city_out.txt")
        kv.putString("json", json)
        log("存入json成功")
    }
  1. 读取json数据分析:
kotlin 复制代码
fun saveJson() {
    val json = JsonTestDataManager.getJsonByFile("city/city_out.txt")
    kv.putString("json", json)
    log("存入json成功")
}

fun readJson() {
    val startTime = System.currentTimeMillis()
    val json = kv.getString("json")
    log("从fastKV 读取json:耗时:${(System.currentTimeMillis() - startTime)} ms ")
    val map = JSON.parseObject(json, object : TypeReference<Map<String, List<String>>>() {})

    val endTime = System.currentTimeMillis()
    map["北京"]?.let {
        log("到FastJson解析结束 耗时:${(endTime - startTime)} ms ${it[0]}")
    }
}

3.读取protobuf数据分析:

kotlin 复制代码
fun saveProtobuf() {
    val bytes = JsonTestDataManager.getBytesFromAssets("city/city_out_p")
    kv.putArray("byte_key", bytes)
    log("存入Protobuf成功")
}

fun readProtobuf() {
    val startTime = System.currentTimeMillis()
    val bytes = kv.getArray("byte_key")
    val model = com.wx.dex.high.level.data_soruce.data.city.protobuf.CityModel._CityModel.parseFrom(bytes)
    val endTime = System.currentTimeMillis()
    log("从FastKV 拿到byte 到解析出protobuf 耗时:${(endTime - startTime)} ${model.mapMap.get("北京")!!.valuesList.get(0)}")
}
  1. 加载dex文件耗时分析:
kotlin 复制代码
fun savePluginDexFile() {
    DynamicManageUtils.getDxFile(context, "d_dex", getDlfn("city_model_lib_dex", 1000)).takeUnless { it.exists() }?.run {
        DynamicManageUtils.copyFileFromAssetsToSD(context, this, "city/city_model_lib_dex")
    }
}

fun loadPluginDexFile() {
    val startTime = System.currentTimeMillis()
    val cityModelImpl = WXClassLoader(DynamicManageUtils.getDxFile(context, "d_dex", getDlfn("city_model_lib_dex", 1000)).absolutePath, null, context.classLoader).getInterface(ICityModel::class.java, "com.wx.city.model.impl.PluginCityModelImpl")
    val endTime = System.currentTimeMillis()
    log(" 加载插件包到使用的 耗时:${(endTime - startTime)} ms ${cityModelImpl.cityMap().get("北京")!!.get(0)}")
}
  1. 使用FastKV + FastJson 输出结果:
    6. 使用FastKV + Protobuf 输出结果:
    7. 使用加载外部插件dex方案读取输出结果:
    8. 测试15次尝试平均值比较结果: 9. 可以看到结果:

首次加载结果,由于FastKV 和 FastJson 和Protobuf 他们其实相对还是太慢了

dex加载数据集速度可以说是比他们

快10倍以上

即便是他们都不是第一次加载结果,也不会比dex加载快:

四、插件化之策略模式方案

1. 上面深入分析研究了dex插件化加载速度是最快的
2. dex文件需要从服务器下载,第一次怎么办?
3. 可以第一次加载代码里面的,当插件文件存在时直接使用插件,这是一种策略思想

kotlin 复制代码
fun strategyMode() {
    // 这里我就不用标准策略模式写法了,用到策略思想
    val startTime = System.currentTimeMillis()
    val file = DynamicManageUtils.getDxFile(context, "d_dex", getDlfn("city_model_lib_dex", 1000))
    val cityModelImpl = if (file.exists()) WXClassLoader(file.absolutePath, null, context.classLoader).getInterface(ICityModel::class.java, "com.wx.city.model.impl.PluginCityModelImpl")
    else CityModelImpl()
    val endTime = System.currentTimeMillis()
    log(" 策略模式使用的 耗时:${(endTime - startTime)} ms ${cityModelImpl.cityMap().get("北京")!!.get(0)}")
}

4. 这样第一次走代码里面默认,有新数据时候下载插件下来,自动加载插件
5. 深度思考:如果开发一个SDK包,默认走本地包里面,需要修改SDK内容时候,直接更新插件,这样不是做到动态化了吗?
6. 再次深度思考:上面SDK的下载逻辑,判断下载插件,下载配置逻辑默认都在包内实现,如果这些下载逻辑,加载插件逻辑需要修改,这块逻辑也做成插件下载下来,真正的SDK插件也可以下载下来,这样不是可以做到全动态化了吗
7. 原来:全动态化原理是这样的,不仅插件可以下载,连接入宿主的那一部分逻辑都可以动态修改的

  1. 看到这里,可以参考我前面的文章,即头部 8篇全动态系列 WXDynamicPlugin 开源框架的文章

五 总结

  1. 本位带领大家探索了 应用配置数据,怎样首次加载最:
    抛弃:Json
    抛弃:Protobuf
    抛弃:SharedPreferences
    抛弃:MMKV
    抛弃:FastKV
    使用:Dex插件加载技术
  2. 怎么动态化使用Dex插件加载策略方案
  3. 深入领悟到全动态插件化是怎么实现的

感谢阅读:

欢迎 关注,点赞、收藏

这里你会学到不一样的东西

相关推荐
simplepeng4 小时前
我的天,我真是和androidx的字体加载杠上了
android
l软件定制开发工作室5 小时前
Jetpack Architecture系列教程之(一)——Jetpack介绍
android jetpack
小猫猫猫◍˃ᵕ˂◍5 小时前
备忘录模式:快速恢复原始数据
android·java·备忘录模式
CYRUS_STUDIO7 小时前
使用 AndroidNativeEmu 调用 JNI 函数
android·逆向·汇编语言
梦否7 小时前
【Android】类加载器&热修复-随记
android
徒步青云8 小时前
Java内存模型
android
今阳8 小时前
鸿蒙开发笔记-6-装饰器之@Require装饰器,@Reusable装饰器
android·app·harmonyos
-优势在我13 小时前
Android TabLayout 实现随意控制item之间的间距
android·java·ui
hedalei13 小时前
android13修改系统Launcher不跟随重力感应旋转
android·launcher
Indoraptor14 小时前
Android Fence 同步框架
android