- 这几年一直忙去项目和家庭,作为快开发10年工程师,经历了移动最辉煌的时代,也经历低落的时段,做过了几万用户-百万等等无数项目,也许将要面临转行等等,在疫情这次大灾难下,让我深深感受我们身处了一个幸福的时代,我们不应该敢于平庸,不能随波逐流,才把最近几年项目中组件化、插件化、热更新、路由、mvp、插拔式服务都会慢慢开源出来,项目代码开源https://gitee.com/applehsp/AndroidHspDemo,model分支,欢迎大家点星,多多评论、多多开源提交建议,支持kotlin、androidx,版主也在学flutter和ios,会分享一些新的博客,网络库地址:
com.applehsp.http:AppleHttpJava:1.0.0,mvp地址依懒com.applehsp.mvp:MvpKotlin:1.0.7。
组件化就是模块分离,模块工程可以独自编译和运行, 首先是清单文件androidmanifest.xml分离,目前大多实现思路无非两套方案:
1.在build.gradle中通过gradle.properties中定义一个变量,然后控制是否模块(com.android.library)或者app(com.android.applicaito),然后在app工程build.gradle中动态控制添加类库方式。
2.我的方案通过groovy做一个插件,然后每个工程gradle.properties中RunAlone属性是否运行模块标记,mainmodulename标记主工程标记,如果是app主工程标记运行,脚本过滤非模块动态添加类库。
project.dependencies.add("api", project.project(':' + taskPro.name))
插件源码是 com.model.buildgradle:model-plugin,已发布到jenter,源码地址https://github.com/apple317/ModelPlugin.git,使用说明如下,将会发布一片如何实现插件、发布jenter、插件源码groovy讲解。
SetUp.1 : 主项目引用编译脚本
在根目录的gradle.properties文件中,增加属性:
mainmodulename=app
其中mainmodulename是项目中的host工程,一般为app
SetUp.2 : 在根目录的gradle文件中配置
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
.....
classpath 'com.model.buildgradle:model-plugin:1.0.0'//添加组件化插件
.....
}
}
allprojects {
repositories {
.....
}
}
.....
SetUp.3 : 拆分组件为module工程
在非app组件的工程目录下新建文件gradle.properties文件,增加以下配置:
RunAlone=true
component=sharecomponent,other(其他模块)
在app组件的工程目录下新建文件gradle.properties文件,增加以下配置,默认自动添加所有其它组件: RunAlone=true
上面三个属性分别对应
SetUp.4 : 在组件和host的build.gradle都增加配置
apply plugin: 'model-plugin'
//注意:不需要在引用com.android.application或者com.android.library
模块通信是组件化中最难的一点,如何做到独立解偶,这里先分享arouter路由实现,然后会在接下来另一个博客分享16年自研发路实现,大体思路跟阿里arouter差不多,一般的话可以直接使用arouter.
如上图在需要地方地址注入,然后需要地方调用如下图:
在这里有个巨坑请注意kotlin和java混编的时候,arouter框架会报各种错误,org.jetbrains.kotlin.kapt3.base.ProcessorWrapper,jdk版本,混编报错等,正确配置如下图,一定要按红线标记重点配置,不然会报错到让人认清不了自我:
模块根本就是互相不影响,互相可以自己实现业务,那么application是app唯一全局单利存活体,平时想初始化推送、图片缓冲、网络配置等等都是在这里做,那么如何业务拆分、独立、解决耦合,目前我查遍大多数网上有两种实现。
1.网上大多实现思路是在基础application中,通过在主工程注入方式,然后各自模块扩张,这样背离解决耦合,app工程就应该是个空壳原理。
2.application离散注册,其实原指一个博客文章对我的启发,为什么不再各种模块根据需要插拔注册,然后在基础application中集中通过反射实现,代码如下:
val mAppLifecycles= ArrayList()
fun getApplicationLifecycles(){
try {
val appInfo: ApplicationInfo = getPackageManager().getApplicationInfo(
getPackageName(), PackageManager.GET_META_DATA
)
if (appInfo.metaData != null) {
for (key in appInfo.metaData.keySet()) {
if ("AppConfig".equals(appInfo.metaData.get(key))) {
mAppLifecycles.add(parseModule(key))
}
}
}
} catch (e: PackageManager.NameNotFoundException) {
throw RuntimeException(
"Unable to find metadata to parse ModularAppConfiger",
e
)
}
}
open fun parseModule(className: String): ApplicationLifecycles {
val clazz: Class<*>
clazz = try {
Class.forName(className)
} catch (e: ClassNotFoundException) {
throw IllegalArgumentException(
"Unable to find ModularAppConfiger implementation",
e
)
}
val module: Any
module = try {
clazz.newInstance()
} catch (e: InstantiationException) {
throw java.lang.RuntimeException(
"Unable to instantiate ModularAppConfiger implementation for $clazz",
e
)
} catch (e: IllegalAccessException) {
throw java.lang.RuntimeException(
"Unable to instantiate ModularAppConfiger implementation for $clazz",
e
)
}
if (module !is ApplicationLifecycles) {
throw java.lang.RuntimeException("Expected instanceof ModularAppConfiger, but found: $module")
}
return module
}
清单文件注册,然后各自模块扩张实现
组件化最核心也是如何通信,模块之间如何通信,最近看过大多数帖子博客以及微信方案,然后我在15年当时方案上升级优化总结,模块各自负责需要对外开发api,这个api组件是所有模块都要继承,如图:
然后在各自模块实现api对外业务逻辑,如下图:
/login/loginserver这个是url,注入到路由之中,那么在调用到是通过path就可以反射找到这个类,然后调用对应业务代码。如下图:
这块有个坑请注意,不要使用arouter中的注解注入autowired,会出现找不到kotlin类。