安卓组件化架构实践

组件化架构

组件化和插件化的目的都是为了解决项目越来越复杂,耦合性高,牵一发而动全身,一个小的改动也要编译十几分钟等问题,两者的区别简单来说组件化是在编译期分模块,插件化是在运行期。一般插件化用于动态修复bug或者动态更新模块,相对来说黑科技更多一些,而相对于插件化,组件化的架构更容易操作,效率高,基于安卓自有特性和gradle的功能,没有插件化那么多的坑,对于大多数应用而言,其优势还是相当明显的。

先看下我们的架构图:

这里写图片描述
App控制中心主要负责组件的添加和删除,路由规则的定义。
module是我们单独开发的模块,如个人中心,详情页,专题页等等
common主要功能就是集合第三方的library,统一引入到项目中

原理与思路:

为每一个module设置library和application两种属性,通过配置文件控制,当集合一起编译时作为library依赖到app控制中心,当独立开发时切换到application属性变成一个普通的module,common作为一个公共的三方库集合,页面跳转基于ARouter路由框架,消息传递用可以用EventBus,当然也可以用Arouter内部的API。

框架概览:

其中main、modulefirst和modulesecond是三个独立的模块,应用的首页在main里,当然也可以指定到其他组件,只需要配一个注解就可以,修改起来非常方便。
当为集合编译时项目是这样的:
这里写图片描述
当为独立开发时项目是这样的:
这里写图片描述

开始搭建

1.创建完工程后,配置可切换library和application属性的文件。

在project根节点下在gradle.properties文件添加以下代码:
# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
isModule=false

然后在每个组件的build.gradle根节点里添加代码:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

2.清单文件的处理

由于每一个组件都是一个独立的APP,都有自己的清单文件,都有自己的Application属性和MainActivity,当合并一起编译时势必会和其他组件的清单文件产生冲突,而我们自己的模块又需要自己开发时用到清单文件,为此,我们的解决方法是:分别建立一个debug文件夹,一个release文件夹,分别放置一个清单文件,不同的工程属性时启用对应的清单文件即可解决,两个清单文件的区别如图所示:
这里写图片描述
然后再build.gradle里添加如下代码:


android {
   /……/
    //这里是我们添加的代码
    sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                //release模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
            }
        }
    }
}

3.解决全局Application和组件自己的Application的冲突问题

应用启动时,系统会为每一个应用创建唯一一个Application对象,其生命周期最长一直到应用结束,我们开发时往往会自己定义一个Application,做一些初始化的操作,这时就要去告诉系统要实例化自己定义的,也就是我们在清单文件的Application节点配置的name属性,那么我们的组件本就是独立的APP,肯定会有自己的Application,如何合并编译,肯定又有问题了,对此,我们的的解决方法是,先创建一个Common库,这个库包含了各种公共的类,如BaseActivity,BaseApplication,Utils等等,每一个组件的Application都继承自Common的BaseApplication,这样组件就可以用自己的Application了,记得在自己的清单文件中做相应修改。为了保证合并编译时只有一个BaseApplication,我们可以这样修改
这里写图片描述
然后在build.gradle里的这一行代码就起作用了:
这里写图片描述

4.对编译版本统一配置,依赖库也统一配置

这样做的目的是为了统一组件的工作环境,避免API版本参差不齐带来的问题,同时也符合规范化管理的要求,而且便于维护,默认情况下,如果是 aar 依赖,gradle 会自动帮我们找出新版本的库而抛弃旧版本的重复依赖。但是如果你使用的是 project 依赖,gradle 并不会去去重,最后打包就会出现代码中有重复的类了。为了避免同一个库依赖两次,我们把所有第三方依赖放到Common库中管理,组件的build.gradle里只需要这样写:
这里写图片描述

5.对App控制中心模块的处理

App模块在项目里负责路由规则定义和组件注册,没有界面,虽然看起来和普通module一样,但是千万不要搞混了,它除了管理功能,还有就是作为整个项目合并编译的端口,通常我们debug编译都是习惯这样
这里写图片描述
这里app就是这样一个作用,双击即可合并所有组件一起编译,在此雄伟的壮举成功之前,我们一定要注意先修改App模块的build.gradle文件如下:
这里写图片描述

代码贴出来

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    if (!isModule.toBoolean()) {
        compile project(':main')
        compile project(':modelfirst')
        compile project(':modelsecond')
    } else {
        compile project(':common')
    }

}

顺便也把全局编译版本统一配置:
在project的build.gradle里这样写

ext{
    // Sdk and tools
    buildToolsVersion = localBuildToolsVersion
    compileSdkVersion = 25
    minSdkVersion = 16
    targetSdkVersion = 25
    versionCode = 1
    versionName = "1.0"
    javaVersion = JavaVersion.VERSION_1_8
    aptCompilerVersion = "1.1.7"
    routerVersion = "1.2.2"
    loggerVersion = "1.15"
}

组件的build.gradle里这样写

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }

   /……/
}

6.最后就是各个组件之间的跳转问题也是最关心的问题。

这一部分,对于不熟悉Router的我真是踩了不少坑,其实总结起来却是非常简单,只是一些小的细节没有注意,你的APP就总是无法跳转成功。先说项目如何配置:
本文章用的是github一个开源路由框架:ActivityRouter里面有详细的说明,另外阿里也开源了一个路由框架:ARouter,有时间的化可以拿过来研究一下,使用起来都比较容易上手的,如果你想自己实现一个路由,可以参考这篇文章:Android路由实现

在project的build.gradle添加代码:

  dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        //这是我们要添加的
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

然后在每一个组件的build.gradle里添加:

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    apt 'com.github.mzule.activityrouter:compiler:1.1.7'
    compile project(':common')
}

如图所示:
这里写图片描述

sync工程一下后,开始处理路由配置
首先是App模块,这个是编译入口,需要管理所有组件,
新建一个类文件
这里写图片描述
注意图中的注解,是@Module,不是@Modules,
然后给MainApplication添加注解,这里注意是@Modules了,如图:
这里写图片描述
接下来处理应用入口的组件,注意APP只是编译控制组件,应用的主页是由另一个组件控制的,这里我定义为main组件,这个地方需要注意,和其他模块略有不同,如图:
这里写图片描述
注意看,这个Activity是没有注解的,同时,其清单文件要添加Launcher属性。
这里写图片描述
对应的定义一个Main类,如图
这里写图片描述

和入口组件不同的是,其他组件的Activity上要加注解:
这里写图片描述
这里注意,注解里的字段一定是和组件module的名称是一致的!
接下来就可以开始跳转了,随意定义几个按钮,然后添加监听里的方法
这里写图片描述
当然路由协议有多种跳转规则,后期我会将其他示例逐步加到框架里。

关于组件间的通信

组件内跳转建议还是采用startActivity,组建间跳转,可以用ActivityRouter的路由协议:
例如:
组件2定义了两个Activity:

@Router("modelsecond")
public class ModelSecondActivity extends BaseActivity{……}
……
……
@Router("modelsecond/:demo")//注意这里“/:demo”即作为此Activity的定位符,路由可以据此找到对应Activity
public class ActivityDemo extends BaseActivity{……}

组件1中有一个Activity:

@Router("modelfirst")
public class ModelFirstActivity extends BaseActivity{}

现在如果想从组件1的ModelFirstActivity 跳转到组件2的ActivityDemo ,那么点击事件里只需要这样写:

 Routers.open(ModelFirstActivity.this, "module://modelsecond/:demo");

如果需要传递intent参数,那么只需要这么写:

Routers.open(ModelFirstActivity.this, "module://modelsecond/:demo?sign=abcdef&name=jack");

在需要获取参数的地方这么写:

getIntent().getStringExtra("sign");
getIntent().getStringExtra("name");

更多细节可以参考这里:【ActivityRouter】
如果单纯的传递消息可以用EventBus

注意事项

  1. 每一个组件,要给自己的所有资源文件制定命名规则,避免和其他组件资源重名,切记
  2. 注意入口组件和其他组件的区别是入口Activity是没有注解的
  3. App控制中心组件也就是编译组件里,@Module和@Modules切记不要记混了
  4. 总之就是细心,再细心,一定要自己动手搞一遍

最后贴出demo地址:【传送门】

参考文章:

  1. http://blog.csdn.net/guiying712/article/details/55213884
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值