天天看点

安卓组件化架构实践

组件化架构

组件化和插件化的目的都是为了解决项目越来越复杂,耦合性高,牵一发而动全身,一个小的改动也要编译十几分钟等问题,两者的区别简单来说组件化是在编译期分模块,插件化是在运行期。一般插件化用于动态修复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 = 
    minSdkVersion = 
    targetSdkVersion = 
    versionCode = 
    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里添加:

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 ,那么点击事件里只需要这样写:

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

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

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