如果你想成就一番伟业,在确立你远大的目标之后,静下心来,认认真真、脚踏实地地开始你的编程吧
(一)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(二)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(三)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构
(四)零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构(五) 大型项目架构:全动态插件化+模块化+Kotlin+协程+Flow+Retrofit+JetPack+MVVM+极限瘦身+极限启动优化+架构示例+全网唯一
(六) 大型项目架构:解析全动态插件化框架WXDynamicPlugin是如何做到全动态化的?
(七) 还在不断升级发版吗?从0到1带你看懂WXDynamicPlugin全动态插件化框架
(八) Compose插件化:一个Demo带你入门Compose,同时带你入门插件化开发
(九) 花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
(十) Compose的全动态插件化框架支持了,已更新到AGP 8.6,Kotlin2.0.20,支持Compose
(十一)按需下载!!全动态插件化框架WXDynamicPlugin解析怎么支持的
一、前言
上面(一)(二)(三)(四)(五)(六)篇文章分别对全动态插件化框架WXDynamicPlugin进行了:
开发背景
,
主要介绍
,
详细说明
,
接入指南
,
项目介绍
,
原理解析
因为插件化本身难度比较大,有一部分人 看懂前面6篇文章
后已经能够上手用了,还有些上手比较困难,主要是看到项目模块比较多,一下就懵逼了:
为什么这么多模块?
为什么要这样设计?
模块这么多,看得眼花缭乱?
.......
本文就从头开始一步一步按照开发一个项目工程的正常顺序来带领着让人更容易看懂上手。
本文介绍思路:
先假设把示例App所涉及到的功能,当作我现在公司项目App, 我们现在要把它 改造成
全动态插件化框架 WXDynamicPlugin的架构
先分析示例App,我们示例App有哪些功能?用到了哪些组件?
自己项目想这样做成全动态插件化 也要进行这一步分析
二、准备工作
- 首先我们先看下所有开发环境,电脑安装JDK 版本,并记得配置好环境变量
2. 在看电脑Android Studio 版本:
- 新建一个工程
这里直接看到
WX-Maven/WX-Host/
下面
前面文章也说过
WX-Code 和 WX-Maven 下实际代码内容一样,目录结构一样,唯一区别接入全动态host代码方式不一样,另外WX-Maven 下所有文件夹和项目工程名多了 maven前缀,用于在同一工程下区分成 2个不同的项目
为了方便不看 源码接入方式 ,方便少看一些模块,我们只看 Maven下面:把 settings.gradle 里面 有关app 的工程模块全部注释掉(原来第2行 到 116行,新版35行到137行)
好,现在我们在 WX-Maven/WX-Host/
下面 建5个工程,目录结构暂时为项目中那样结构。
这里代码不贴全部:因为都在项目里面,按照这个思路步骤配置下去就可以了(后面一样)
设置 gradle-wrapper.properties文件内 gradle-7.1-bin.zip
distributionUrl=https://services.gradle.org/distributions/gradle-7.1-bin.zip
配置主工程build.gradle下
com.android.tools.build:gradle:4.2.1
配置主工程build.gradle下 repositories中添加
maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' }
找到Android Studio Setting ,配置Gradle JDK 配置为11:如下:
- 打开工程下 local.properties
以下为我本地电脑配置,需要添加一行下面一句,并改成自己电脑配置:
workingDirPath=D:\android_software\android_sdk\android_sdk\build-tools\32.0.0\
好 以上是 项目环境配置完了,接下来就是代码设计编写接入了
三 、宿主APK代码编写
在 WX-Maven/WX-Host/
下面1个sample工程 4个sample-lib模块
- 宿主代码设计解读参考: 这里面有详细解读
零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构(四) 在这里面可以看到 我前言中表格里面的所有依赖 都配置在这里面了。
另外,下面文章解读也有很大帮助
大型项目架构:解析全动态插件化框架WXDynamicPlugin是如何做到全动态化的?
整个宿主APK 就这么点代码,真正属于全动态插件化框架 接入的部分就下面4个
io.github.wgllss:Wgllss-Dynamic-Host-Constant-Lib:1.0.0.5
io.github.wgllss:Wgllss-Dynamic-Host-Download-Lib:1.0.0.5
io.github.wgllss:Wgllss-Dynamic-Host-Lib-Impl:1.0.0.8
io.github.wgllss:Wgllss-Dynamic-Host-Version-Lib:1.0.0.5
- 宿主app里面
VersionImpl
,FaceImpl
的部分配置,得和插件里面对应一样,等插件写好了再来配置这里,后面回过头来看这里配置。 sample-lib/maven-wgllss-common-re-library
lib中: 公共代码带res资源的模块lib ,涉及到加载loading,权限框架提示资源,网络错误提示文案资源 全局 application的context,这些是一个app 开发必备的,基本也不会频繁修改,直接放在公共模块中,把它集成在宿主里面,让插件模块直接用它(在插件那边可以搜索到有依赖),但插件模块就不用打包这部分代码进去了,这样插件模块打包体积也就最少了。同时里面添加了前言表格中框架必备依赖maven-wgllss-business-re-library
lib中 加入了 前言中表格中的 示例工程需要的依赖,同时 Constants内的 defaultImgID:只是 实例一下 这么写,可以这样直接从插件中调用到宿主 资源(可以查看到),该类中其他的也是一样WX-Maven/WX-Host/sample
下SampleApplication
class SampleApplication : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
LogTimer.initTime(this)//这个打印不重要,不是必须
AppGlobals.sApplication = this//示例用来全局 application
//下面一步 执行下载 并加载插件,里面全动态化逻辑都在里面
WXDynamicLoader.instance.installPlugin(base, FaceImpl(), VersionImpl())
}
}
四、插件编写
接下来来到插件的编写,来到 在 WX-Maven/WX-Plugin/
下面:
- 插件的设计参考我前面写的接入详细指南 设计成14个插件文件,里面有对应14个插件对应的模块
- 插件加载流程图参考我前面写过的 大型项目架构:解析全动态插件化框架WXDynamicPlugin是如何做到全动态化的?
五、配置、打包、运行
- 前面四、插件编写 下1,2两步把插件化14个模块看懂之后
找到WX\WX-Maven\WX-Plugin\Maven-Wgllss-Dynamic-Plugin-Sample\maven-wgllss-sample-loader-version工程下
LoaderVersionImpl
类
先配置版本文件,
默认全部都是1000,我因为开源几个月来 全动态配置升级过版本号,所以不全都是1000
配置好开始打包生成14个文件出来
具体怎么打包请看零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构(三)
/** 特别提示:
* 1:首次启动用 宿主里面的 VersionImpl,当有版本更新时用 WX-Plugin/Maven-Wgllss-Dynamic-Plugin-Sample/maven-wgllss-sample-loader-version
* 2:首次时两个地方文件配置成一样
* 3: 以后每次修改插件,升级插件 ,需要修改 WX-Plugin/Maven-Wgllss-Dynamic-Plugin-Sample/maven-wgllss-sample-loader-version 下面配置
**/
class LoaderVersionImpl : ILoaderVersion {
override fun getV() = 1005 //总版本号 只要下面每个地方改一下 此处版本号要往上加+1,下面可同一时间改多个,上面加一下版本号
override fun isMustShowLoading() = false //下次下载插件 是否显示主下载loading 页面
override fun getClfd() = Triple( //配置loading 页插件实现 及版本号 对应 maven-wgllss-sample-loader-version 工程
"com.wgllss.dynamic.impl.ILoadHomeImpl", "loading", 1000
)
override fun getClmd() = Triple("", "", 0)
override fun getCdlfd() = Triple("", "", 0)
/** 上面两个方法 默认 没有,如果需要动态 配置,见下面 已经注释掉的 方式配置 **/
// override fun getClmd() = Triple("com.wgllss.dynamic.plugin.loader.LoaderManagerImpl", "class_loader_impl_dex", 1000)
//
// override fun getCdlfd() = Triple("com.wgllss.dynamic.plugin.download_face.DownLoadFaceImpl", "classes_downloadface_impl_dex", 1000)
/**
* 下面要添加删除 map内内容 map的key 不能自定义
* 即:COMMON,WEB_ASSETS,COMMON_BUSINESS,RUNTIME,MANAGER,RESOURCE_SKIN,HOME不能动
*/
override fun getMapDLU() = linkedMapOf(
COMMON to Pair(
"classes_common_lib_dex", 1001
), //Maven-Wgllss-Dynamic-Plugin-Common-Library 插件工程 和 版本号
WEB_ASSETS to Pair(
"classes_business_web_res", 1000
), //maven-wgllss-sample-assets-source-apk 插件工程 和版本号
COMMON_BUSINESS to Pair(
"classes_business_lib_dex", 1003
), //maven-wgllss-sample-business-library 插件工程 和 版本号
RUNTIME to Pair(
"classes_wgllss_dynamic_plugin_runtime", 1000
), //Maven-Wgllss-Dynamic-Plugin-RunTime-Apk 插件工程 和 版本号
MANAGER to Pair(
"classes_manager_dex", 1000
), // Maven-Wgllss-Dynamic-Plugin-Manager 插件工程 和 版本号
RESOURCE_SKIN to Pair(
"classes_common_skin_res", 1000
), // maven-wgllss-sample-skin-resource-apk 插件工程 和 版本号
HOME to Pair("classes_home_dex", 1002)//maven-wgllss-sample-ui-home 插件工程 和 版本号
)
override fun getOthers() = mutableMapOf(
"classes_other_dex" to 1000, //maven-wgllss-sample-ui-other-lib 插件工程 和 版本号
"classes_other_res" to 1000, //maven-wgllss-sample-ui-other 插件工程 和 版本号
"classes_other2_dex" to 1001, //maven-wgllss-sample-ui-other2-lib2 插件工程 和 版本号
"classes_other2_res" to 1000 //maven-wgllss-sample-ui-other2 插件工程 和 版本号
)
}
- 将打包出来的14个文件中的
classes_loading_dex
放在 前面宿主工程WX-Maven/WX-Host/sample
下assets
下面并改名字成loading_1000
- 现在14个插件打包出来了,再回到宿主
WX-Maven/WX-Host/sample
下找到VersionImpl
,把前面1中LoaderVersionImpl
类中的版本配置内容,一模一样配置在VersionImpl
里面配置好,作为宿主Apk 第一次请求下载和加载的14个插件的版本号,后面每次升级版本号,就用不到这个WX-Maven/WX-Host/sample
下修改VersionImpl
类了, 后升级版本请看零反射,零HooK,全动态化,插件化框架,全网唯一结合启动优化的插件化架构(三),该文中有具体介绍过 - 好,现在14个插件文件出来了,默认插件
loading_1000
也放到宿主assets
下了,宿主第一次内置默认版本配置(VersionImpl
)也配好了,那么这14个文件放在哪儿,配置下载地址在哪儿?
回到宿主WX-Maven/WX-Host/sample
下找到FaceImpl
进行配置:
class FaceImpl : IDynamicDownLoadFace {
private var baseXL: String = ""
// override fun getHostL() = "http://192.168.3.21:8080/assets/WXDynamicPlugin/"
// override fun getHostL() = "http://192.168.1.9:8080/assets/WXDynamicPlugin/"
//todo 自己本地搭一个服务器,或者 自己服务器 或者 像我一样在gitee上面在自己的项目下建一个文件当作服务器 供下载,
// 切记不要往往我的 gitee 项目上面推
override fun getHostL() = "https://gitee.com/wgllss888/WXDynamicPlugin/raw/master/WX-Resource/"
/** 0:WXDynamicPlugin 动态化插件框架 理论上已经做到了可以完全不动宿主,但是如果一定要动宿主 可以提供以下思路:
* 1:可以根据 宿主版本号得到 宿主版本支持的 的插件,
* 2:当宿主必须 需要升级时,升级后原版本的插件不可用了,插件配置在新宿主版本文件夹下面,原宿主版本文件夹可可以先动态配置 在启动页 升级下载新的宿主
* @example 宿主版本 10000 版本支持的插件 放在服务端 WXDynamicPlugin/10000/ 文件夹下 20000版本的插件放在 WXDynamicPlugin/20000/下面
*/
override fun getBaseL(): String {
if (TextUtils.isEmpty(baseXL)) {
baseXL = StringBuilder().append(getHostL()).append(DeviceIdUtil.getDeviceId()).append("/").append(BuildConfig.VERSION_CODE).append("/").toString()
}
return baseXL
}
override fun isDebug() = false
override fun getOtherDLU() = realUrl("vc")
/**
* 下面要添加删除 map内内容 map的key 不能自定义
* 即:VERSION,COMMON,WEB_ASSETS,COMMON_BUSINESS,HOME,RESOURCE_SKIN,RUNTIME,MANAGER,FIRST,CLMD,CDLFD不能动
*/
override fun getMapDLU() = mutableMapOf(
VERSION to realUrl("classes_version_dex"), // 对应 maven-wgllss-sample-loader-version打包后插件
COMMON to realUrl("classes_common_lib_dex"), // 对应 Maven-Wgllss-Dynamic-Plugin-Common-Library打包后插件
WEB_ASSETS to realUrl("classes_business_web_res"), // 对应 maven-wgllss-sample-assets-source-apk打包后插件
COMMON_BUSINESS to realUrl("classes_business_lib_dex"), // 对应 maven-wgllss-sample-business-library打包后插件
HOME to realUrl("classes_home_dex"), // 对应 maven-wgllss-sample-ui-home打包后插件
RESOURCE_SKIN to realUrl("classes_common_skin_res"), // 对应 maven-wgllss-sample-skin-resource-apk打包后插件
RUNTIME to realUrl("classes_wgllss_dynamic_plugin_runtime"),// 对应 Maven-Wgllss-Dynamic-Plugin-RunTime-Apk打包后插件
MANAGER to realUrl("classes_manager_dex"), // 对应 Maven-Wgllss-Dynamic-Plugin-Manager打包后插件
FIRST to realUrl("classes_loading_dex"), // 对应 maven-wgllss-sample-ui-loading打包后插件
CLMD to realUrl("class_loader_impl_dex"), // 对应 Maven-Wgllss-Dynamic-Plugin-Loader-Impl打包后插件
CDLFD to realUrl("classes_downloadface_impl_dex") // 对应 Maven-Wgllss-Dynamic-Plugin-DownloadFace-Impl打包后插件
)
/** 1:首次启动用 宿主里面的 VersionImpl,当有版本更新时用 WX-Plugin/Maven-Wgllss-Dynamic-Plugin-Sample/maven-wgllss-sample-loader-version
* 2:首次时两个地方文件配置成一样
* 3: 以后每次修改插件,升级插件 ,需要修改 WX-Plugin/Maven-Wgllss-Dynamic-Plugin-Sample/maven-wgllss-sample-loader-version 下面配置
**/
override fun getLoadVersionClassName() = "com.wgllss.loader.version.LoaderVersionImpl"
private fun realUrl(name: String) = StringBuilder().append(getBaseL()).append(name).toString()
}
- 上面下载地址配置固定在宿主里面一份代码,如果想不发版动态修改里面内容请参考我的文章
大型项目架构:解析全动态插件化框架WXDynamicPlugin是如何做到全动态化的?
里面有介绍怎么动态修改这个地方内容,以及怎么调试?,怎么切换环境? - 把14个插件文件 上传到5中配置的服务器地址里面
- 打包宿主
WX-Maven/WX-Host/sample
工程 安装宿主APK,然后就可以正常运行了。
六、总结
- 本文从开发一个项目0到1的顺序的视角来看示例app,让人更容易看懂:
主要从示例项目
功能分析,
准备工作 ,
宿主APK代码编写
插件编写,
配置打包运行 - 本文涉及到头部6篇文章作为基础,那6篇文章里面都有详细介绍这些