项目演示:
1.组件化重构效果
这里先看下我们重构前后的框架图比较:
重构前:
重构后
-
ft_xxx
表示业务层模块lib_xxx
表示基础库模块
重构后的架构图如下:
重构前的代码业务封装在宿主app中,业务耦合严重,如果修改一个业务模块,需要对整个app进行完整测试,测试工作量巨大 而重构后,我们只需要对单一app进行独立调试即可。
重构后的框架结构:所有的业务组件之间通讯都通过ft_base_service
进行通讯
2.组件化重构准则
- 1.单一业务可以单独调试,也可以作为lib提供给宿主app使用
- 2.同一级别的模块不允许直接调用,比如我们的ft_home组件不允许直接调用ft_login组件,不然组件化的意义就不存在了
- 3.组件间通讯不能直接使用显示的class文件跳转,可以考虑很用ARouter框架进行解耦
- 4.每个组件可打包为aar或者jar上传到maven私服,宿主使用的时候,直接引用私服中aar包即可
能做到以上几点,你的app就可以称为一个组件化框架的app了。
3.组件化重构思路
- 1.拆:拆代码,拆资源,拆构建 由于所有业务和资源都耦合在宿主app中,所以需要将代码和资源拆开到对应模块中 当然我们的构建build.gradle也需要拆分到不同模块中
- 2.接:对外提供接口 组件化之间不能直接通讯,需要使用暴露接口的方式对外通讯
- 3.测:反复测试 重构后代码,需要反复测试,防止出现意想不到的bug
4.组件化重构过程
这里我以登录业务ft_login
为例子:
1.步骤1:首先新建一个业务模块ft_login
,然后在宿主app中将登录功能相关联的代码和资源抽离到ft_login
中
2.步骤2:将和登录构建相关的依赖分配到ft_login
构建中。
3.步骤3:单独调试功能实现
if(isRunAlone.toBoolean()){ apply plugin:'com.android.application' }else{ apply plugin:'com.android.library' } android { compileSdkVersion 33 buildToolsversion "33.0.0" defaultConfig { if(isRunAlone.toBoolean()){ applicationId 'com.anna.ft_login' } ... } sourceSets { main { java { srcDirs = ['src/main/java'] } resources { srcDirs = ['src/main/res'] } aidl { srcDirs = ['src/main/aidl'] } manifest { if(isRunAlone.toBoolean()){ srcFile 'src/main/manifest/AndroidManifest.xml' }else { srcFile 'src/main/AndroidManifest.xml' } } } } } def dependList = [rootProject.depsLibs.okhttp, rootProject.depsLibs.gson, rootProject.depsLibs.appcompact, rootProject.depsLibs.design, rootProject.depsLibs.eventbus, rootProject.depsLibs.arouterapi, ':lib_network',':lib_common_ui',':ft_base_service'] dependencies { if(!isRunAlone.toBoolean()){ dependList.each { String depend -> depend.startsWithAny(':lib',':ft')? compileOnly(project(depend)):compileOnly(depend){ switch (depend){ case rootProject.depsLibs.arouterapi: exclude group: 'com.android.support' break; } } } }else { dependList.each { String depend -> depend.startsWithAny(':lib',':ft')? implementation(project(depend)):implementation(depend) { switch (depend) { case rootProject.depsLibs.arouterapi: exclude group: 'com.android.support' break; } } } } //arouter注解处理器 annotationProcessor rootProject.depsLibs.aroutercompiler testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' }
@H_404_618@单独调试状态下注意四点:
- 1.引用application插件
- 2.引入applicationId
- 3.引入不同给的sourceSets构建路径
- 4.引入的库单独调试状态下需要使用
implementation
导入,不能使用compileOnly
实现上面四点,只要打开isRunAlone就可作为一个单独app运行了。
4.步骤4:组件间通讯
这里,我们引入一个ft_base_service
模块,这个模块用来实现组件间通讯用,需要调用别的业务模块都需要使用这个模块才能通讯、 业务模块与ft_base_service
之间通讯使用的是路由ARouter
: 关于ARouter
的使用可以参考这篇文章:
Android开源系列-组件化框架Arouter-(一)使用方式详解
引入ARouter依赖
:
android { javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName(),AROUTER_GENERATE_DOC: "enable"] } } } //arouter核心api implementation rootProject.depsLibs.arouterapi //arouter注解处理器 annotationProcessor rootProject.depsLibs.aroutercompiler
@H_404_618@创建LoginService:
public interface LoginService extends IProvider { boolean hasLogin(); void login(Context context); }
@H_404_618@
- 2.在
ft_login
业务模块中实现LoginService接口
注意这里因为使用了ARouter注解,所以也需要引入ARouter
依赖
@Route(path = "/login/login_service")
public class LoginServiceImpl implements LoginService {
Context context;
@Override
public boolean hasLogin() {
return UserManager.getInstance().hasLogined();
}
@Override
public void login(Context context) {
LoginActivity.start(context);
}
@Override
public void init(Context context) {
Log.d("TAG","LoginServiceImpl is init");
}
}
@H_404_618@
- 3.在
ft_base_service
模块中对LoginService
接口进行依赖注入
public class LoginImpl { @Autowired(name = "/login/login_service") public LoginService mLoginService; private static LoginImpl mLoginImpl = null; public static LoginImpl getInstance() { if (mLoginImpl == null) { synchronized (LoginImpl.class) { if (mLoginImpl == null) { mLoginImpl = new LoginImpl(); } return mLoginImpl; } } return mLoginImpl; } private LoginImpl(){ ARouter.getInstance().inject(this); } public boolean hasLogin(){ return mLoginService.hasLogin(); } public void login(Context context){ mLoginService.login(context); } }
@H_404_618@笔者使用了一个单例类LoginImpl,在构造器中对
LoginService
依赖注入ARouter.getInstance().inject(this);
然后宿主app或者其他模块引用登录业务功能时,需要依赖
ft_base_service
模块,并使用LoginImpl
的接口即可。这里要说明下,平时我们使用的四大组件跳转也可以使用这个方式来处理,在服务接口中定义跳转接口即可。当然也可以使用Arouter的Activity跳转方式或者Fragment实例获取方式
Gradle筑基篇(六)-使用Maven实现组件化类库发布
这里我们封装了一个通用组件发布库:
apply plugin: 'maven' uploadArchives { repositories { mavendeployer { // 是否快照版本 def isSnapShot = Boolean.valueOf(MAVEN_IS_SNAPSHOT) def versionName = MAVEN_VERSION if (isSnapShot) { versionName += "-SNAPSHOT" } // 组件信息 pom.groupId = MAVEN_GROUP_ID pom.artifactId = MAVEN_ARTIFACTID pom.version = versionName // 快照仓库路径 snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) { authentication(userName: MAVEN_USERNAME,password: MAVEN_USERNAME) } // 发布仓库路径 repository(url: uri(MAVEN_RELEASE_URL)) { authentication(userName: MAVEN_USERNAME,password: MAVEN_USERNAME) } println("###################################" + "\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging + "\nrepository =" + (isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL) + "\n###################################" ) } } }
@H_404_618@然后在对应的组件下面引用:
apply from:file('../maven.gradle')
@H_404_618@发布的时候直接在
Gradle
面板中点击uploadArchives
任务即可
经过上面几个步骤就基本完成了login组件的封装并发布,且对外提供了login组件接口 其他组件也是按照上面的逻辑进行重构
5.组件化重构总结
组件化不仅是一种架构,更是一种思想,架构是可以变得,但是核心思想却是统一的,在拆分代码的时候,要注意模块的颗粒度,不是颗粒度越小就越好,模块分离的好,后期对组件改造会有很大帮助, 关于组件化的文章就讲到这里。
于是在我自己弄得以后,整理了《Android 架构学习手册》+《深入理解Gradle框架》学习笔记,根据自己学习中所做的一些笔录来整的,主要也是方便后续好复习翻阅,省掉在去网上查找的时间,以免在度踩坑,如果大家有需要的可以 直接点击此处↓↓↓进行参考 学习!!!
Android 架构学习手册
深入理解Gradle框架
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。