前言
前面几篇文章咱们讲解了一个云音乐app的根底库搭建,今天咱们就来对这个app进行组件化代码重构
组件化根底库封装系列文章如下:
Android组件化开发(一)–Maven私服的搭建
Android组件化开发(二)–网络恳求组件封装
Android组件化开发(三)–图片加载组件封装
Android组件化开发(四)–进程保活组件的封装
Android组件化开发(五)–完整版音乐播映组件的封装
Android组件化开发(六)– 短视频播映组件封装
Android组件化开发(七)–从零开始教你分析项目需求并完成
项目地址:github.com/ByteYuhb/an…
项目演示:
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:单独调试功能完成
-
3.1:在
gradle.properties
中创立一个全局变量:isRunAlone=true
-
3.2:在
build.gradle
中:
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'
}
单独调试状态下留意四点:
- 1.引证application插件
- 2.引进applicationId
- 3.引进不同给的sourceSets构建途径
- 4.引进的库单独调试状态下需求运用
implementation
导入,不能运用compileOnly
完成上面四点,只需打开isRunAlone就可作为一个单独app运行了。
4.过程4:组件间通讯
这儿,咱们引进一个ft_base_service
模块,这个模块用来完成组件间通讯用,需求调用其他事务模块都需求运用这个模块才能通讯、
事务模块与ft_base_service
之间通讯运用的是路由ARouter
:
关于ARouter
的运用能够参阅这篇文章:
Android开源系列-组件化结构Arouter-(一)运用方法详解
- 1.创立
ft_base_service
,在这个模块中:创立一个LoginService
接口继承IProvider
引进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
创立LoginService:
public interface LoginService extends IProvider {
boolean hasLogin();
void login(Context context);
}
- 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");
}
}
- 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);
}
}
笔者运用了一个单例类LoginImpl,在构造器中对LoginService
依靠注入
ARouter.getInstance().inject(this);
然后宿主app或许其他模块引证登录事务功能时,需求依靠ft_base_service
模块,并运用LoginImpl
的接口即可。
这儿要说明下,平常咱们运用的四大组件跳转也能够运用这个方法来处理,在服务接口中界说跳转接口即可。当然也能够运用Arouter的Activity跳转方法或许Fragment实例获取方法
- 5.代码打包aar上传到
maven私服
:
关于这块maven私服更多内容能够参阅这篇文章:
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###################################"
)
}
}
}
然后在对应的组件下面引证:
apply from:file('../maven.gradle')
发布的时分直接在Gradle
面板中点击uploadArchives
任务即可
通过上面几个过程就基本完成了login组件的封装并发布,且对外供给了login组件接口 其他组件也是按照上面的逻辑进行重构
更多详细信息能够自己拿到项目源代码检查。
5.组件化重构总结
组件化不仅是一种架构,更是一种思维,架构是能够变得,但是中心思维却是统一的,在拆分代码的时分,要留意模块的颗粒度,不是颗粒度越小就越好,模块别离的好,后期对组件改造会有很大协助,
关于组件化的文章就讲到这儿,组件化重构的项目已经上传到Github。
后面会出一期插件化
的项目改造。敬请期待。