花式高阶:插件化之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. 深入领悟到全动态插件化是怎么实现的

感谢阅读:

欢迎 关注,点赞、收藏

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

相关推荐
JINGWHALE12 小时前
设计模式 创建型 抽象工厂模式(Abstract Factory)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·抽象工厂模式
JINGWHALE15 小时前
设计模式 行为型 模板方法模式(Template Method Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·模板方法模式
Hello.Reader11 小时前
MyBatis 性能优化
性能优化·mybatis
我命由我1234512 小时前
Android Process 问题:NoSuchMethodError,No static method myProcessName()
android·java·java-ee·android studio·安卓·android-studio·安卓开发
TroubleMaker13 小时前
OkHttp源码学习之Authenticator
android·java·okhttp
yodala14 小时前
C++中的表达式
android·c++
susu108301891114 小时前
android studio实现圆形图片
android·ide·android studio
JINGWHALE114 小时前
设计模式 行为型 中介者模式(Mediator Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·中介者模式
hedalei15 小时前
Android13工具修改wifi mac地址以后没有更新的问题
android·wifi·mac地址
Mac Zhu15 小时前
Android中的蓝牙:BLE、经典蓝牙
android