引言
上一篇我们学习了热修中的ClassLoader方案设计,主要是用来如何加载插件的;
在了解了宿主加载不同插件的姿势后,我们接下来看如何实现插件编程,同时了解著名的腾讯团队之一(PS:据说是腾讯视频等的某个事业群)是如何用插件化来实现动态化的设计的~
插件编程
大致流程
1)插件工程开发,其实就是一个app工程,实现插件业务
2)开发过程,插件需要不断调试,看下宿主加载和使用是否正常,那么需要把插件频繁拷贝到宿主能用的地方(如:asset等),这个可以用脚本实现
3)宿主验证
上面是简要的流程,接下来我们来看每一个流程的细节介绍和简单实现。
插件工程
这里是一个app模块,里面主要是实现插件自己的业务,比如:酷狗App里面,有听歌/直播等模块,每一个模块其实就是一个插件apk
插件相关脚本
在开发完插件功能后,插件apk需要拷贝到宿主的工程或者其他地方给宿主加载使用,这个过程涉及脚本的自动化,比如shadow的sample-manager插件apk的脚本(注意下面脚本都是在宿主的gradle文件中实现的):
1)宿主的gradle文件中,把插件的拷贝时机挂载在宿主的build过程中,这个在开发过程中比较方便,具体实现:
tasks.whenTaskAdded { task ->
if (task.name == "generateDebugAssets") {
generateAssets(task, 'debug')
}
if (task.name == "generateReleaseAssets") {
generateAssets(task, 'release')
}
}
复制代码
2)generateAssets 的实现:
/***
* 1)generateAssetsTask, 系统task的锚点
* 2)buildType,debug/release
* */
def generateAssets(generateAssetsTask, buildType) {
println "daviAndroid generateAssets"
def moduleName = 'sample-manager'
def pluginManagerApkFile = file("${project(":sample-manager").getBuildDir()}" + "/outputs/apk/${buildType}/"
+ "${moduleName}-${buildType}.apk")
//createCopyTask 挂在在 《generateAssetsTask, 系统task的锚点》的前面
generateAssetsTask.dependsOn createCopyTask(
':sample-manager',
buildType,
moduleName,
'pluginmanager.apk',
pluginManagerApkFile,
"assemble${buildType.capitalize()}"
)
}
复制代码
3)createCopyTask 实现:
//把 制定模块的apk(如:sample-manager )拷贝到 本工程的 build/generated/assets/xx/
def createCopyTask(projectName, buildType, name, apkName, inputFile, taskName) {
println "daviAndroid createCopyTask"
def outputFile = file("${getBuildDir()}/generated/assets/${name}/${buildType}/${apkName}")
outputFile.getParentFile().mkdirs()
return tasks.create("copy${buildType.capitalize()}${name.capitalize()}Task", Copy) {
group = 'build'
description = "复制${name}到assets中."
from(inputFile.getParent()) {
include(inputFile.name)
rename { outputFile.name }
}
into(outputFile.getParent())
}.dependsOn("${projectName}:${taskName}")
}
复制代码
PS1:注意这里拷贝到了宿主的《build/generated/assets/xx/》,在宿主中通过openAsset方式读取,所以工程需要配置下assets的指向
sourceSets {
debug {
assets.srcDir('build/generated/assets/sample-manager/debug/')
}
release {
assets.srcDir('build/generated/assets/sample-manager/release/')
}
}
复制代码
PS2:完整代码戳这里>>
验证
在上面已经完成了插件的开发,插件的拷贝;下面就是宿主在拷贝的制定目录进行加载和使用,关于加载的方式上一节有讲不同的方式,可以根据自己的业务需要进行选择,具体代码戳这里>>
插件化实现动态设计
背景
我们都知道腾讯的shadow框架中有一个特点就是全动态设计,插件框架的代码(如:插件的配置升级等业务)成为了插件的一部分,实现迭代不再受宿主打包了旧版本插件框架所限制
插件的配置升级动态化实现
插件在迭代过程中,会有在不同的业务下用不同的插件,那么这里就需要实现插件的下载/更新等逻辑;
正常情况下这些逻辑是写在宿主里面的,但是shadow就把这这部分逻辑通过插件的思维动态化起来了,这样后续策略等要调整,就不会面临不能调整的困惑
下面我们来看下shadow是怎么实现的,讲解的过程是根据代码展开,代码位置戳这里>>
实现过程
1)设计思想
a)宿主:以依赖接口的方式,加载插件里面的接口实现,实现插件的动态化
b)接口:乔接宿主和插件
c)插件:实现插件的下载业务/预加载其他插件等
当下载业务/预加载逻辑等需变动的时候,只需要改变插件的实现,然后重新下发即可
2)插件模块
下面是工程架构情况:
a)关于constant,一些常量
b)关于dynamic-manager
跨进程Service的实现机制,插件的加载等业务都是基于这样的IPC方案作为承载实现
c)关于manager
插件加载/更新/下载等具体实现类,如:加载插件的类型/插件的bean/插件zip的配置描述等
d)关于dynamic-host
首先要注意的是,这个模块对于插件来说只是编译的时候生效,然后是不打入插件的,和宿主的公共模块
主要是一些宿主要用的,插件也要供的公共模块,比如:自定义的ApkClassLoader,宿主需要用来加载插件,然后sample-manager插件管理器需要用来加载其他插件
3)宿主模块
下面是工程架构情况:
1)关于app
宿主启动模块,涉及启动触发插件更新时机/插件拷贝到本地等
2)关于constant,一些常量(和插件公共模块,具体见上面的插件部分)
3)关于dynamic-host
上面插件部分介绍了主要的功能,但是和插件区别的是这里模块是打到了宿主里面
4)关于commons-io
文件IO相关库
结尾
哈哈,该篇就写到这里(一起体系化学习,一起成长)
Tips
更多精彩内容,请关注 ”DaviAndroid“ 微信公众号