Android组件化框架搭建
csdh11 2025-03-11 14:49 15 浏览
背景
当一个项目经过N手人开发,N个产品经理的蹂躏,N长时间的维护,此时一定存在大量代码冗余、业务耦合、项目臃肿,资源文件大把重复等等,不堪重负。当需要增加新功能或者修改之前某个功能的时候,我相信很多同仁都说只敢增加,不敢随意地去删除、修改原有的代码,因为不知道哪些有用,哪些没有用。不但增加了维护成本,也在无形中增加了APK的体积,浪费了资源。在此背景下,就衍生出了模块化、组件化的概念。目前也已经有很多优秀的案例,我就踩在巨人的肩膀上搭建了符合组件业务的组件化框架。
头条不允许内嵌GitHub的网址 所以无法配置超链接 需要源码的小伙伴可以留言,无偿奉献给小伙伴们!
一.浅谈模块
其基本理念就是,把常用的功能、控件、基础类、第三方库、权限等公共部分抽离封装,把业务拆分成N个模块进行独立(module)的管理,而所有的业务组件都依赖于封装的基础库,业务组件之间不做依赖,这样的目的是为了让每个业务模块能单独运行。而在APP层对整个项目的模块进行组装,拼凑成一个完整的APP。借助路由(Arouter)来对各个业务组件之间的跳转,通过消息(eventbus)来做各个业务模块之间的通信。
模块化的好处:
- 解耦 只要封装做得好,实际开发中会省去大量地重复代码的coding。
- 结构清晰、层次明显,对后面的维护也是极其容易的。
- 每个业务模块可独立运行,单独提测,节省开发时间。
二.基础搭建
先来一张整个项目构思图
根据项目构思图搭建的项目结构图
下面逐一介绍每个模块的功能:
- app模块: app壳没有任何功能主要就是集成每个业务组件,最终打包成一个完整的APK。app壳的gradle做如下配置,根据配置文件中的 isModule 字段来依赖不同的业务组件
...
dependencise{
//公用依赖包
implementation project(':common_base')
if (!Boolean.valueOf(rootProject.ext.isModule)) {
//main模块
implementation project(':module_main')
implementation project(':module_market')
implementation project(':module_wan_android')
}
}
...
- common_base模块:功能组件主要负责封装公共部分,如第三方库加载、网络请求、数据存储、自定义控件、各种工具类等。
为了防止重复依赖问题,所有的第三方库都放在该模块加载,业务模块不再做任何的第三方库依赖,只做common_base库的依赖即可。
common模块无论在什么情况下都是以 library 的形式存在,所有的业务组件都必须依赖于common
其结构如下:
在commong的 gradle 中引入项目中使用的所有第三方库,业务组件就不用再去逐一引入
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
...
dependencies {
// 在项目中的libs中的所有的.jar结尾的文件,都是依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])
//把implementation 用api代替,它是对外部公开的, 所有其他的module就不需要添加该依赖
api rootProject.ext.dependencies["appcompat_v7"]
api rootProject.ext.dependencies["constraint_layout"]
api rootProject.ext.dependencies["cardview-v7"]
api rootProject.ext.dependencies["recyclerview-v7"]
api rootProject.ext.dependencies["support-v4"]
api rootProject.ext.dependencies["design"]
api rootProject.ext.dependencies["support_annotations"]
//MultiDex分包方法
api rootProject.ext.dependencies["multidex"]
//黄油刀
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
api rootProject.ext.dependencies["butterknife"]
//Arouter路由
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
api rootProject.ext.dependencies["arouter_api"]
api rootProject.ext.dependencies["arouter_annotation"]
//eventbus 发布/订阅事件总线
api rootProject.ext.dependencies["eventbus"]
//网络
api rootProject.ext.dependencies["novate"]
//日志
api rootProject.ext.dependencies["logger"]
//fastJson
api rootProject.ext.dependencies["fastjson"]
//沉浸栏
api rootProject.ext.dependencies["barlibrary"]
//banner
api rootProject.ext.dependencies["banner"]
//图片加载
api rootProject.ext.dependencies["picasso"]
//lombok
api rootProject.ext.dependencies["lombok"]
api rootProject.ext.dependencies["lombokJavax"]
}
- 业务组件,在集成模式下它以 library 的形式存在。在组件开发模式下它以 application 的形式存在,可以单独独立运行。
业务组件完整的 gradle 如下:
if (Boolean.valueOf(rootProject.ext.isModule)) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'com.jakewharton.butterknife'
...
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//公用依赖包
implementation project(':common_base')
//Arouter路由
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
//黄油刀
annotationProcessor rootProject.ext.dependencies["butterknife_compiler"]
}
- 配置文件,对项目中的第三库、app的版本等配置
//全局统一配置文件
ext {
//true 每个业务Module可以单独开发
//false 每个业务Module以lib的方式运行
//修改之后需要Sync方可生效
isModule = false
//版本号
versions = [
applicationId : "com.wss.amd", //应用ID
versionCode : 1, //版本号
versionName : "1.0.0", //版本名称
compileSdkVersion : 27,
buildToolsVersion : "27.0.3",
minSdkVersion : 17,
targetSdkVersion : 23,
androidSupportSdkVersion: "27.1.1",
constraintLayoutVersion : "1.1.1",
runnerVersion : "1.0.1",
espressoVersion : "3.0.1",
junitVersion : "4.12",
annotationsVersion : "24.0.0",
multidexVersion : "1.0.2",
butterknifeVersion : "8.4.0",
arouterApiVersion : "1.4.0",
arouterCompilerVersion : "1.2.1",
arouterannotationVersion: "1.0.4",
eventbusVersion : "3.0.0",
novateVersion : "1.5.5",
loggerVersion : "2.2.0",
fastjsonVersion : "1.1.54",
barlibraryVersion : "2.3.0",
picassoVersion : "2.71828",
bannerVersion : "1.4.10",
javaxVersion : "1.2",
lombokVersion : "1.16.6",
greendaoVersion : "3.2.2",
]
dependencies = ["appcompat_v7" : "com.android.support:appcompat-v7:${versions["androidSupportSdkVersion"]}",
"constraint_layout" : "com.android.support.constraint:constraint-layout:${versions["constraintLayoutVersion"]}",
"runner" : "com.android.support.test:runner:${versions["runnerVersion"]}",
"espresso_core" : "com.android.support.test.espresso:espresso-core:${versions["espressoVersion"]}",
"junit" : "junit:junit:${versions["junitVersion"]}",
"support_annotations" : "com.android.support:support-annotations:${versions["annotationsVersion"]}",
"design" : "com.android.support:design:${versions["androidSupportSdkVersion"]}",
"support-v4" : "com.android.support:support-v4:${versions["androidSupportSdkVersion"]}",
"cardview-v7" : "com.android.support:cardview-v7:${versions["androidSupportSdkVersion"]}",
"recyclerview-v7" : "com.android.support:recyclerview-v7:${versions["androidSupportSdkVersion"]}",
//方法数超过65535解决方法64K MultiDex分包方法
"multidex" : "com.android.support:multidex:${versions["multidexVersion"]}",
//路由
"arouter_api" : "com.alibaba:arouter-api:${versions["arouterApiVersion"]}",
"arouter_compiler" : "com.alibaba:arouter-compiler:${versions["arouterCompilerVersion"]}",
"arouter_annotation" : "com.alibaba:arouter-annotation:${versions["arouterannotationVersion"]}",
//黄油刀
"butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
"butterknife" : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
//事件订阅
"eventbus" : "org.greenrobot:eventbus:${versions["eventbusVersion"]}",
//网络
"novate" : "com.tamic.novate:novate:${versions["novateVersion"]}",
//日志
"logger" : "com.orhanobut:logger:${versions["loggerVersion"]}",
//fastJson
"fastjson" : "com.alibaba:fastjson:${versions["fastjsonVersion"]}.android",
//沉浸式状态栏
"barlibrary" : "com.gyf.barlibrary:barlibrary:${versions["barlibraryVersion"]}",
//banner
"banner" : "com.youth.banner:banner:${versions["bannerVersion"]}",
//图片加载
"picasso" : "com.squareup.picasso:picasso:${versions["picassoVersion"]}",
//lombok
"lombokJavax" : "javax.annotation:javax.annotation-api:${versions["javaxVersion"]}",
"lombok" : "org.projectlombok:lombok:${versions["lombokVersion"]}",
//数据库
"greenDao" : "org.greenrobot:greendao:${versions["greendaoVersion"]}",
]
}
最后别忘记在工程的中 build.gradle引入该配置文件
apply from: "config.gradle"
修改isModule字段之后 需要Sysn才会生效
三.搭建过程中遇到的问题
- Application 、全局Context、 Activity管理问题
在功能组件即Demo中的common_base封装BaseApplication,在BaseApplication对第三方库初始化、全局Context的获取等操作。在BaseActivity中对Activity进行添加和移除的管理
//BaseApplicion
public class BaseApplication extends Application {
...
//全局唯一的context
private static BaseApplication application;
//Activity管理器
private ActivityManage activityManage;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
application = this;
//MultiDex分包方法 必须最先初始化
MultiDex.install(this);
}
public void onCreate() {
super.onCreate();
activityManage = new ActivityManage();
initARouter();
initLogger();
}
/**
* 获取全局唯一上下文
*
* @return BaseApplication
*/
public static BaseApplication getApplication() {
return application;
}
}
//BaseActivity
public abstract class BaseActivity extends Activity {
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//加入Activity管理器
BaseApplication.getApplication().getActivityManage().addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//将Activity从管理器移除
BaseApplication.getApplication().getActivityManage().removeActivityty(this);
}
}
- AndroidManifest的管理
我们知道APP在打包的时候最后会把所有的AndroidManifest进行合并,所以每个业务组件的Activity只需要在各自模块的AndroidManifest中注册即可。如果业务组件需要独立运行,则需要单独配置一份AndroidManifest,在gradle的sourceSets根据不同的模式加载不同的AndroidManifest文件。
gradle配置
...
android {
...
sourceSets {
main {
if (Boolean.valueOf(rootProject.ext.isModule)) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
//排除java/debug文件夹下的所有文件
exclude '*module'
}
}
}
}
}
...
注意:在配置Gradle的时候 manifest.srcFile... manifest 是小写的
其中集成模式加载的Manifest中不能设置Application和程序入口:
//集成模式下Manifest
//组件模式下Manifest
需要注意的是如果在组件开发模式下,组件的Applicaion必须继承自BaseApplicaion
- 不同组件之间的跳转
业务组件之间没有依赖,不能通过常规的Intent显示的进行跳转,这个时候就需要引入路由的概念
利用阿里的[ARouter]对需要跳转的页面做配置
gradle配置
android {
...
defaultConfig {
...
//Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
}
dependencies{
...
//Arouter路由
annotationProcessor rootProject.ext.dependencies["arouter_compiler"]
}
目标页面配置
@Route(path = "/wan/WanMainActivity")
public class WanMainActivity extends ActionBarActivity implements IWanMainView,
OnRcyItemClickListener {
...
}
跳转
...
ARouter.getInstance()
.build("/wan/WanMainActivity")
.navigation();
...
- 不同组件之间通信
可以利用第三方 如[EventBus]对消息进行管理。在common_base组件中的Base类做了对消息的简单封装,子类只需要重写regEvent()返回true即可对事件的注册,重写onEventBus(Object)即可对事件的接收。
public abstract class BaseActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (regEvent()) {
EventBus.getDefault().register(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (regEvent()) {
EventBus.getDefault().unregister(this);
}
}
/**
* 子类接收事件 重写该方法
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventBus(Object event) {
}
/**
* 需要接收事件 重写该方法 并返回true
*/
protected boolean regEvent() {
return false;
}
- Butterknife的问题
在library中使用butterknife会存在找不到的问题。
推荐使用8.4.0版本,用R2代替R,onClick中使用if else不要使用switch case即可解决问题 。
public class HomeFragment extends BaseMvpFragment implements IHomeView, OnRcyItemClickListener {
@BindView(R2.id.banner)
Banner banner;
@BindView(R2.id.recycle_view)
RecyclerView recyclerView;
...
@OnClick({R2.id.tv_title, R2.id.btn_open})
public void onClick(View v) {
if (v.getId() == R.id.tv_title) {
//do something
} else if (v.getId() == R.id.btn_open) {
//do something
}
}
}
- 资源文件冲突问题
目前没有比较好的约束方式,只能通过设置资源的前缀来防止资源文件冲突,然后在提交代码的时候对代码进行检查是否规范来控制。
该文章于2018年首次发表,历经3年多的变革,其中使用的一些技术以及开发技巧可能有更新,请各位读者按照实际开发过程中的情况酌情调优~
头条不允许内嵌GitHub的网址 所以无法配置超链接 需要源码的小伙伴可以留言,无偿奉献给小伙伴们!
相关推荐
- IDEA界面太丑??尝试一下这几个插件
-
前言IntelliJIDEA主要用于支持Java、Scala、Groovy等语言的开发工具,同时具备支持目前主流的技术和框架,擅长于企业应用、移动应用和Web应用的开发。IntelliJi...
- 小巧 Vue 页面滚动进度条组件ScrollProgress
-
今天给大家分享一个轻量级Vue.js全屏滚动进度条组件VueScrollProgress。vue-scroll-progress一款基于vue.js构建的页面滚动进度条组件,...
- 基于vue实现可视化拖拽编辑器,页面生成工具,提升前端开发效率
-
项目介绍基于vue实现的可视化拖拽编辑器,实现页面生成工具,提升前端开发效率。可以基层到移动端项目作为自定义json直接生成UI页面。项目特点功能特点...
- 优秀 vue+heyui 后端管理系统HeyUI-Admin
-
今天再给小伙伴们推荐一款成熟的企业中后台管理系统HEYUI-Admin。heyui-admin基于vue.js和heyui组件库构建的后台管理系统。包含基础表单/表格功能,拓展组件(图表、富文本编辑...
- 响应式 Vue.js 前端组件化框架Xvue-UI
-
今天给小伙伴们推荐一款超不错的Vue轻量级组件框架XVueUI。xvue-ui基于vue2.x构建的响应式前端组件化框架。轻量级、易于上手,提供...
- 《基于SpringBoot+Vue的在线视频系统设计与实现》开题报告
-
【计算机毕业设计案例】基于SpringBoot+Vue的在线视频系统设计与实现_哔哩哔哩_bilibili...
- 超好用 Vue.js 图片裁切组件Vue-ImgCutter
-
今天给小伙伴们分享一个超棒的Vue图片任意裁剪插件VueImgCutter。vue-img-cutter基于vue2.x构建的轻量级剪切图片组件。支持移动图像、放大缩小图片、任意移动图片、固定比...
- Vue 3 进阶用法:异步组件(vue 异步组件原理)
-
一、代码分割一个大型前端应用,如果所有代码都放在单一文件,体积会特别大,下载时间长,白屏时间久,用户体验差。...
- 源码补丁神器—patch-package(源码助手怎么用)
-
作者:张浩一、背景vue项目中使用vue-pdf第三方插件预览pdf,书写业务代码完美运行,pdf文件内容正常预览无问题。后期需求有变,业务需求增加电子签章功能。这个时候pdf文件的内容可以显示出...
- 经验分享:Vue2 项目升级 Vue3 + Element Plus,借助Deepseek手动升级
-
Vue3出来好久了,我开发的项目还在使用Vue2框架,一般情况下不考虑升级,但是最近需要接入工作流程引擎之类的,看了下Vue2生态下操作空间不是很好,那索性尝试升级Vue3吧。一番操作下来,升级成功,...
- 34K Star!史上最全JavaScript资源库 awesome-javascript
-
34KStar!史上最全JavaScript资源宝库大揭秘引言在GitHub上,有一个备受瞩目的JavaScript资源仓库,以其全面的内容和精心的分类,成为了众多开发者的必备参考。这个拥有超过...
- 基于 Vue.js 磁片栅格布局组件VueGridLayout
-
#头条创作挑战赛#今天给大家分享一个超优秀的vue.js拖拽栅格布局插件VueGridLayout。...
- 6款高颜值 Vue3 PC端UI组件库(vue3开发组件库)
-
马上到国庆了,还没学习或者想学习vue3的小伙伴们有安排上没?这次推荐几个比较流行的VUE3UI组件库,合理利用,又或者学习借鉴都是不错的选择。1、element-pluselement-plus...
- 高性能 vue.js+ztree 树形组件Vue-GiantTree
-
今天给大家分享一款超棒的Vue海量数据渲染树形组件VueGiantTree。vue-giant-tree基于ztree封装的Vue树形组件。轻松实现大数据高性能渲染,适合海量数据渲染场景。zTr...
- 【推荐】2024年推荐的6款开源免费 Vue 后台管理系统模板,建议收藏
-
前言在现今的软件开发领域,...
- 一周热门
- 最近发表
-
- IDEA界面太丑??尝试一下这几个插件
- 小巧 Vue 页面滚动进度条组件ScrollProgress
- 基于vue实现可视化拖拽编辑器,页面生成工具,提升前端开发效率
- 优秀 vue+heyui 后端管理系统HeyUI-Admin
- 响应式 Vue.js 前端组件化框架Xvue-UI
- 《基于SpringBoot+Vue的在线视频系统设计与实现》开题报告
- 超好用 Vue.js 图片裁切组件Vue-ImgCutter
- Vue 3 进阶用法:异步组件(vue 异步组件原理)
- 源码补丁神器—patch-package(源码助手怎么用)
- 经验分享:Vue2 项目升级 Vue3 + Element Plus,借助Deepseek手动升级
- 标签列表
-
- mydisktest_v298 (34)
- document.appendchild (35)
- 头像打包下载 (61)
- acmecadconverter_8.52绿色版 (39)
- word文档批量处理大师破解版 (36)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- parsevideo (33)
- 个人网站源码 (37)
- centos7.4下载 (33)
- mysql 查询今天的数据 (34)
- intouch2014r2sp1永久授权 (36)
- 先锋影音源资2019 (35)
- jdk1.8.0_191下载 (33)
- axure9注册码 (33)
- pts/1 (33)
- spire.pdf 破解版 (35)
- shiro jwt (35)
- sklearn中文手册pdf (35)
- itextsharp使用手册 (33)
- 凯立德2012夏季版懒人包 (34)
- 冒险岛代码查询器 (34)
- 128*128png图片 (34)
- jdk1.8.0_131下载 (34)
- dos 删除目录下所有子目录及文件 (36)