Android 组件化,从入门到不可自拔

=======================================================================

组件化可以说是 Android 中级开发工程师必备技能了,能有效解决许多单一项目下开发中出现的问题。

并且我要强调的是,组件化真的不难,还没搞过的小伙伴不要怂。

什么是组件化


组件,顾名思义,“组装的零件”,术语上叫做软件单元,可用于组装在应用程序中。

所以,组件化,要更关注可复用性、更注重关注点分离、功能单一、高内聚、粒度更小、是业务上能划分的最小单元,毕竟是“组装的零件”嘛!

从这个角度上看,组件化的粒度,似乎要比模块化的粒度更小。

不过,我个人认为,要把组件化拆分到如此小的粒度,不可能,也没有必要。在组件化项目的实际开发中,组件化的粒度,是要比模块化的粒度更大的。

组件化的好处


首先要说的是,上述模块化的好处,组件化都有,不再赘述;上述模块化的弊端,组件化都给解决了,具体如下:

  1. 组件,既可以作为 library,又可以单独作为 application,便于单独编译单独测试,大大的提高了编译和开发效率;

  2. (业务)组件,可有自己独立的版本,业务线互不干扰,可单独编译、测试、打包、部署;

  3. 各业务线共有的公共模块可开发为组件,作为依赖库供各业务线调用,减少重复代码编写,减少冗余,便于维护;

  4. 通过 gradle 配置文件,可对第三方库进行统一管理,避免版本冲突,减少冗余;

  5. 通过 gradle 配置文件,可实现 application 与 library 灵活组合与拆分,可以更快速的响应需求方对功能模块的选择。

组件化实践

=======================================================================

首先要说明的是,下述是一个简单的不能再简单的组件化案例,只求帮助大家搭建起组件化的架构,功能上极其简约。

九层之台,起于累土。我们这就开始搭组件化的架构吧!

组件化架构


先上一张组件化项目整体架构图

其中的“业务组件”,既可以作为 application 单独打包为 apk,又可以作为 library 灵活组合为综合一些的应用程序。

大多数开发者做组件化时面对的业务需求,都是上面这种情况。

我司的需求略有不同,不是将子业务组件组合为整体应用程序,而是反其道而行之,需要将已上线项目拆分给不同的业务公司使用,在不同业务系统中,项目的逻辑和代码会有区别,且版本不统一。

基于此,我搭建项目架构如下图所示,其中“m_moudle_main”是公司主要的、且逻辑和代码相同的业务组件,“b_moudle_north”和“b_moudle_south”是拆分出来的业务组件,管理各自私有的逻辑和代码,且版本有差别。

从Android工程看,结构如下图所示:

注:取moudle名,手动加上“b_” “m_” “x_”这样的前缀,只是为了便于分辨组件层次。

统一配置文件


在项目根目录下,自建 config.gradle 文件,对项目进行全局统一配置,并对版本和依赖进行统一管理,源码如下:

/**

  • 全局统一配置

*/

ext {

/**

  • module开关统一声明在此处

  • true:module作为application,可单独打包为apk

  • false:module作为library,可作为宿主application的组件

*/

isNorthModule = false

isSouthModule = false

/**

  • 版本统一管理

*/

versions = [

applicationId : “com.niujiaojian.amd”, //应用ID

versionCode : 100, //版本号

versionName : “1.0.0”, //版本名称

compileSdkVersion : 28,

minSdkVersion : 21,

targetSdkVersion : 28,

androidSupportSdkVersion: “28.0.0”,

constraintlayoutVersion : “1.1.3”,

runnerVersion : “1.1.0-alpha4”,

espressoVersion : “3.1.0-alpha4”,

junitVersion : “4.12”,

annotationsVersion : “28.0.0”,

appcompatVersion : “1.0.0-beta01”,

designVersion : “1.0.0-beta01”,

multidexVersion : “1.0.2”,

butterknifeVersion : “10.1.0”,

arouterApiVersion : “1.4.1”,

arouterCompilerVersion : “1.2.2”,

arouterAnnotationVersion: “1.0.4”

]

dependencies = [

“appcompat” : “androidx.appcompat:appcompat:${versions[“appcompatVersion”]}”,

“constraintlayout” : “androidx.constraintlayout:constraintlayout:${versions[“constraintlayoutVersion”]}”,

“runner” : “androidx.test🏃${versions[“runnerVersion”]}”,

“espresso_core” : “androidx.test.espresso:espresso-core:${versions[“espressoVersion”]}”,

“junit” : “junit:junit:${versions[“junitVersion”]}”,

//注释处理器

“support_annotations” : “com.android.support:support-annotations:${versions[“annotationsVersion”]}”,

“design” : “com.google.android.material:material:${versions[“designVersion”]}”,

//方法数超过65535解决方法64K MultiDex分包方法

“multidex” : “androidx.multidex:multidex:2.0.0”,

//阿里路由

“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” : “com.jakewharton:butterknife:${versions[“butterknifeVersion”]}”,

“butterknife_compiler”: “com.jakewharton:butterknife-compiler:${versions[“butterknifeVersion”]}”

]

}

然后在project的build.gradle中引入config.gradle文件:

apply from: “config.gradle”

基础公共组件


基础公共组件 common 将一直作为 library 存在,所有业务组件都需要依赖 common 组件。

common 组件主要负责封装公共部分,如网络请求、数据存储、自定义控件、各种工具类等,以及对第三方库进行统一依赖等。

下图是我的 common 组件的包结构图:

前文有言,common 组件还负责对第三方库进行统一依赖,这样上层业务组件就不需要再对第三方库进行重复依赖了,其 build.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”]

api rootProject.ext.dependencies[“constraintlayout”]

api rootProject.ext.dependencies[“junit”]

api rootProject.ext.dependencies[“runner”]

api rootProject.ext.dependencies[“espresso_core”]

//注释处理器,butterknife所必需

api rootProject.ext.dependencies[“support_annotations”]

//MultiDex分包方法

api rootProject.ext.dependencies[“multidex”]

//Material design

api rootProject.ext.dependencies[“design”]

//黄油刀

api rootProject.ext.dependencies[“butterknife”]

annotationProcessor rootProject.ext.dependencies[“butterknife_compiler”]

//Arouter路由

annotationProcessor rootProject.ext.dependencies[“arouter_compiler”]

api rootProject.ext.dependencies[“arouter_api”]

api rootProject.ext.dependencies[“arouter_annotation”]

}

业务组件


业务组件在 library 模式下,向上组合为整体性项目;在 application 模式下,可独立运行。

其 build.gradle 源码如下:

if (Boolean.valueOf(rootProject.ext.isModule_North)) {

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(‘:x_module_common’)

implementation project(‘:m_module_main’)

//黄油刀

annotationProcessor rootProject.ext.dependencies[“butterknife_compiler”]

//Arouter路由

annotationProcessor rootProject.ext.dependencies[“arouter_compiler”]

}

至此,组件化架构的搭建就算完成了。

可还有几个问题,是组件化开发中必须要关注的,也是项目做组件化改造时可能会遭遇的难点,我们一起来看看吧。

组件化必须要关注的几个问题

===============================================================================

Application


在 common 组件中有 BaseAppliaction,提供全局唯一的 context,上层业务组件在组件化模式下,均需继承于 BaseAppliaction。

/**

  • 基础 Application,所有需要模块化开发的 module 都需要继承自此 BaseApplication。

*/

public class BaseApplication extends Application {

//全局唯一的context

private static BaseApplication application;

@Override

protected void attachBaseContext(Context base) {

super.attachBaseContext(base);

application = this;

//MultiDexf分包初始化,必须最先初始化

MultiDex.install(this);

}

@Override

public void onCreate() {

super.onCreate();

initARouter();

}

/**

  • 初始化路由

*/

private void initARouter() {

if (BuildConfig.DEBUG) {

ARouter.openLog(); // 打印日志

ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)

}

ARouter.init(application);// 尽可能早,推荐在Application中初始化

}

/**

  • 获取全局唯一上下文

  • @return BaseApplication

*/

public static BaseApplication getApplication() {

return application;

}

applicationId 管理


可为不同组件设置不同的 applicationId,也可缺省,在Android Studio中,默认的 applicationId 与包名一致。

组件的 applicationId 在其 build.gradle 文件的 defaultConfig 中进行配置:

if (Boolean.valueOf(rootProject.ext.isModule_North)) {

//组件模式下设置applicationId

applicationId “com.niujiaojian.amd.north”

}

manifest.xml 管理


组件在 library 模式和 application 模式下,需要配置不同的 manifest.xml 文件,因为在 application 模式下,程序入口 Activity 和自定义的 Application 是不可或缺的。

在组件的 build.gradle文件 的 android 中进行 manifest 的管理:

/*

  • java插件引入了一个概念叫做SourceSets,通过修改SourceSets中的属性,

  • 可以指定哪些源文件(或文件夹下的源文件)要被编译,

  • 哪些源文件要被排除。

  • */

sourceSets {

main {

if (Boolean.valueOf(rootProject.ext.isModule_North)) {//apk

manifest.srcFile ‘src/main/manifest/AndroidManifest.xml’

} else {

manifest.srcFile ‘src/main/AndroidManifest.xml’

java {

//library模式下,排除java/debug文件夹下的所有文件

exclude ‘*module’

}

}

}

}

资源名冲突问题


资源名冲突问题,相信大家多多少少都遇到过,以前最常见的就是第三方 sdk 导致的资源名冲突了。

这个问题没有特别好的解决办法,只能通过设置资源名前缀 resourcePrefix 以及约束自己开发习惯进行解决。

资源名前缀 resourcePrefix ,是在 Project 的 build.gradle 中进行设置的:

/**

  • 限定所有子类xml中的资源文件的前缀

  • 注意:图片资源,限定失效,需要手动添加前缀

  • */

subprojects {

afterEvaluate {

android {

resourcePrefix “${project.name}_”

}

}

}

这样设置完之后,string、style、color、dimens 等中资源名,必须以设置的字符串为前缀,而 layout、drawable 文件夹下的 shape 的 xml 文件的命名,必须以设置的字符串为前缀,否则会报错提示。

另外,资源前缀的设置对图片的命名无法限定,建议大家约束自己的开发习惯,自觉加上前缀。

建议:将 color、shape、style 这些放在基础库组件中去,这些资源不会太多,且复用性极高,所有业务组件又都会依赖基础库组件。

Butterknife R2 问题


Butterknife 存在的问题是控件 id 找不到,只要将 R 替换为 R2 即可解决问题。

需要注意的是,在如下代码示例外的位置,不要这样做,保持使用 R 即可,如 setContentView(R.layout.b_module_north_activity_splash)

public class SplashActivity extends BaseActivity {

@BindView(R2.id.btn_toMain)

Button btnToMain;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.b_module_north_activity_splash);

ButterKnife.bind(this);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

(备注:Android)**

[外链图片转存中…(img-kEqbQrCd-1713763927561)]

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-fZdMhbhI-1713763927562)]

这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值