Android组件化-基础框架搭建

android studio 下载 | 2019-03-18 16:25

安卓组件化开发是老生常谈的问题,基础的模块化开发教程很多,本系列教程展现从零开始,到整个系统搭建的过程,设计项目组件化结构、MVP设计模式、组件间通信路由框架、WebSocket网络交互基础库的设计、推送服务基础库的设计、UI统一风格基础库的设计、数据库交互基础库的设计、以及业务相关的实际应用场景问题。

系列教程Android组件化-基础框架搭建Android组件化-组件间通信BRouterAndroid组件化-MVP设计模式Android组件化-推送服务Android模块化-推送服务端Android组件化-风格统一&主题变色

资源冲突

单模块调试

组件间通信

项目由一个空壳模块app、一个主界面app-main模块、若干app-xxx业务模块组成、一个公共库lib-common、若干基础库组成,借本结构如下

使用Android Studio作为IDE开发,项目模块基本文件结构如下:

项目搭建主要分三步,搭建基础库模块lib-xxx、全局公共库模块lib-common,搭建业务应用模块app-xxx,整合业务模块到app空壳模块。

新建一个模块作为lib,在AndroidStudio中 File->New->New Module...->Android Library,LibraryName为db,ModuleName为lib-db,如图:

lib-db模块中build.gradle修改如下,

// 用来定义此模块是lib库模块还是应用模块

apply plugin: 'com.android.library'

compileSdkVersion rootProject.ext.compileSdkVersion

defaultConfig {

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

versionCode rootProject.ext.versionCode

versionName rootProject.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

buildTypes {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:26.1.0'

testImplementation 'junit:junit:4.12'

此时lib-db 未引入任何第三方库,rootProject.ext.compileSdkVersion为项目根目录build.gradle中定义的统一配置,方便做全局修改。根目录build.gradle如下:

// SDK Tool Version

minSdkVersion = 21

targetSdkVersion = 26

compileSdkVersion = 26

buildToolsVersion = '25.0.3'

// Build Version

versionCode = 1

versionName = "1.0"

// java version

javaVersion = JavaVersion.VERSION_1_8

// 第三方依赖的版本

supportVersion = "26.1.0"

butterknifeVersion = "8.8.1"

gsonVersion = "2.8.5"

rxJavaVersion = "2.2.2"

rxAndroidVersion = "2.1.0"

rxBindingVersion = "1.0.0"

rxPermissionVersion = "0.10.2"

retrofitVersion = "2.4.0"

okHttpVersion = "3.11.0"

eventBusVersion = "3.1.1"

greenDaoVersion = "3.2.2"

lottieVersion = "2.6.1"

buildscript {

repositories {

dependencies {

classpath 'com.android.tools.build:gradle:3.0.1'

// 后期框架引入诸多第三方库,需要添加更多远程仓库

allprojects {

repositories {

mavenCentral()

task clean(type: Delete) {

delete rootProject.buildDir

// 统一配置android support version

subprojects {

project.configurations.all {

resolutionStrategy.eachDependency { details ->

if (details.requested.group == 'com.android.support'

&& !details.requested.name.contains('multidex')) {

details.useVersion "26.1.0"

* lib库模块中引入第三方库需注意,假如此库中 方法/View控件 需要在应用模块中使用,则此依赖需要使用api引入,如 *

api "com.thirdpart.app:app:1.0"

类似地新建其他几个lib库模块lib-log、lib-push、lib-apptool、lib-socket。

lib-common和基础库同样是lib库模块,搭建的方法相同,不同点在于lib-common模块通过 'compile project' 依赖其它所有的基础库模块,并直接被其它应用模块所依赖,实现向应用模块提供服务。

新建lib-common后,build.gradle配置如下:

apply plugin: 'com.android.library'

compileSdkVersion rootProject.ext.compileSdkVersion

defaultConfig {

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

versionCode rootProject.ext.versionCode

versionName rootProject.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

buildTypes {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

// Support Libary

compile "com.android.support:design:$rootProject.supportVersion"

compile "com.android.support:cardview-v7:$rootProject.supportVersion"

compile "com.android.support:appcompat-v7:$rootProject.supportVersion"

compile "com.android.support:recyclerview-v7:$rootProject.supportVersion"

compile "com.android.support.constraint:constraint-layout:1.1.3"

testImplementation 'junit:junit:4.12'

// 其它基础库模块

compile project(':lib-ui')

compile project(':lib-log')

compile project(':lib-push')

compile project(':lib-router')

compile project(':lib-socket')

compile project(':lib-apptool')

api "com.google.code.gson:gson:$rootProject.gsonVersion"

api "io.reactivex.rxjava2:rxjava:$rootProject.rxJavaVersion"

api "io.reactivex.rxjava2:rxandroid:$rootProject.rxAndroidVersion"

// RxBinding

api "com.jakewharton.rxbinding:rxbinding:$rootProject.rxBindingVersion"

api "com.jakewharton.rxbinding:rxbinding-appcompat-v7:$rootProject.rxBindingVersion"

api "com.jakewharton.rxbinding:rxbinding-recyclerview-v7:$rootProject.rxBindingVersion"

api "com.jakewharton.rxbinding:rxbinding-design:$rootProject.rxBindingVersion"

// RxPermission

api "com.github.tbruyelle:rxpermissions:$rootProject.rxPermissionVersion"

api "com.squareup.okhttp3:okhttp:$rootProject.okHttpVersion"

api "com.squareup.okhttp3:logging-interceptor:$rootProject.okHttpVersion"

api "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"

api "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"

api "com.squareup.retrofit2:adapter-rxjava:$rootProject.retrofitVersion"

// EventBus

api "org.greenrobot:eventbus:$rootProject.eventBusVersion"

// RefreshLayout

api 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-14'

api 'com.scwang.smartrefresh:SmartRefreshHeader:1.0.3'//没有使用特殊Header,可以不加这行

api("com.airbnb.android:lottie:$rootProject.lottieVersion") {

exclude group: 'com.android.support'

业务模块如app-mine(个人中心)、app-message(消息中心)等既可作为应用模块独立运行,也可作为库模块被空壳app依赖,新建业务模块 File->New Module->Phone & Tablet Module,

app-message模块中build.gradle配置如下

// 模块切换至应用模块或库模块

if (moduling.toBoolean()) {

apply plugin: 'com.android.application'

apply plugin: 'com.android.library'

compileSdkVersion rootProject.ext.compileSdkVersion

defaultConfig {

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

versionCode rootProject.ext.versionCode

versionName rootProject.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

buildTypes {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

// 为避免资源冲突,为不同模块的资源加上前缀,资源文件前缀手动加入

resourcePrefix 'message_'

// 库模块和应用模块使用不同的Manifest.xml和Application

sourceSets {

if (moduling.toBoolean()) {

// 注意,粘贴代码时mainfest容易变成Manifest

manifest.srcFile 'src/main/debug/AndroidManifest.xml'

println '[Module-Message]: Appling Application...'

manifest.srcFile 'src/main/AndroidManifest.xml'

exclude 'debug/**'

println '[Module-Message]: Appling Library...'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:support-v4:26.1.0'

// 依赖 common 库模块

compile project(':lib-common')

如上的gradle配置涉及到单组件运行调试、组件间资源文件冲突。

粘贴代码时 manifest.srcFile 容易变成 Manifest.srcFile

空壳app是作为应用模块的“组合车间”,通过依赖不同的/所有的应用模块,将各个功能模块的功能聚合起来,实现app应有的功能,app模块中包含:

build.gradle

app的名称、图标

全局的Application文件

build.gradle如下:

apply plugin: 'com.android.application'

compileSdkVersion rootProject.ext.compileSdkVersion

defaultConfig {

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

versionCode rootProject.ext.versionCode

versionName rootProject.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

// jni配置

abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'

buildTypes {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

// 资源文件前缀

resourcePrefix 'app_'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

if (moduling.toBoolean()) {

compile project(':lib-common')

// 依赖需要的应用模块

compile project(':app-main')

compile project(':app-mine')

compile project(':app-message')

app名称、图标配置在空壳中方便全局修改;

app还包含Application文件,在此做一些初始化操作:

public class AppApplication extends CommonApplication {

public void onCreate() {

super.onCreate();

BLog.d("[App]: Application Starting...");

initRouter();

if (shouldInit()) {

PushClient.getInstance().init(this)..setListener(new PushHandler());

除此之外,空壳app不包含任何逻辑、业务代码,登录注册页、欢迎页、主界面包含在app-main中,也是作为应用模块被app依赖。app模块是真正意义上的空壳。

app-message模块里builld.gradle开头的moduling配置在gradle.properties中,为boolean类型,

# debug settings

moduling=false

通过配置moduling的值,实现app-message作为独立应用模块或库模块的切换。

当build.gradle的apply plugin配置为'com.android.application'时,app-message作为应用模块:

apply plugin: 'com.android.application'

此时模块图标为

当build.gradle的apply plugin配置为'com.android.library'时,app-message作为库模块:

apply plugin: 'com.android.library'

此时模块图标为

在应用模块的build.gradle中有如下配置:

// 库模块和应用模块使用不同的 Manifest.xml 和 Application

sourceSets {

if (moduling.toBoolean()) {

// 注意,粘贴代码时mainfest容易变成Manifest

manifest.srcFile 'src/main/debug/AndroidManifest.xml'

println '[Module-Message]: Appling Application...'

manifest.srcFile 'src/main/AndroidManifest.xml'

exclude 'debug/**'

println '[Module-Message]: Appling Library...'

当模块为独立应用模块时,模块使用 'src/main/debug/'下的AndroidManifest.xml,当模块作为库模块被app空壳依赖时,使用'src/main'下的AndroidManifest.xml。

'src/main/debug/AndroidManifest.xml'配置如下:

<?xml version="1.0" encoding="utf-8"?>

package="org.blackist.modulize.message">

<application

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/message_app_name"

android:roundIcon="@mipmap/ic_launcher_round"

android:supportsRtl="true"

android:theme="@style/message_AppTheme">

<activity android:name=".view.MessageActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

单模块调试时需要有独立的启动Activity、图标、app名称,如果需要独立的Application进行初始化操作,也在此配置。

'src/main/AndroidManifest.xml'配置如下:

<?xml version="1.0" encoding="utf-8"?>

package="org.blackist.modulize.message">

<application>

<activity android:name=".view.MessageActivity" />

</application>

</manifest>

作为库模块被依赖时,只需要在Manifest中注册activity、service等即可。

单模块开发、调试的时候,activity的配置只注册在debug下的Manifest,请记得手动拷贝至main下Manifest中。

不同模块中资源文件难免会有相似的资源文件,比如app-message有个listicon的drawable、app-main中也有个listicon的drawable,非单模块调试的时候需要合并资源文件,聚合在app空壳中,此时会引起资源冲突。

一种比较好的实践就是在各模块中添加资源前缀,尽量避免冲突,这要求项目组成员遵守规范,即资源命名为messagelisticon和mainlisticon。

xml中资源可以被android studio检查,如string.xml或color.xml中的资源,但图片类的资源不会被检查,需要开发者严格遵守约定。

组件化开发还有个问题就是,在各应用模块中使用if-else判断资源id,而不是switch,

if(R.id.message_list_icon == id) {

} else if (R.id.message_list_text == id) {

因为应用模块(app-xxx)依赖库模块(lib-xxx),所以应用模块可以直接使用库模块中的View控件和方法,而应用模块之间项目隔离,无法直接使用其它模块的控件和方法,需要使用组件间通信工具来协助完成。

使用广播、EventBus会有一些相应的问题,比较好的一种实践就是阿里巴巴开源的ARouter以及一些自定义的组件间通信框架如Brouter,组件间通信在组件化开发中经常使用。

主界面有三部分主页、消息中心、个人中心,主页一般在核心的业务逻辑模块中,消息中心页在app-message模块中,个人中心页在app-mine模块中。

app-main中MainActivity负责处理三个页面的展示和切换,即一个Activity包含三个Fragment,代码如下:

private BaseFragment currentFragment;

private BaseFragment mainFragment;

private BaseFragment mineFragment;

private BaseFragment messageFragment;

BRouterRes roomRes = BRouter.push(

getApplicationContext(),

BRouterReq.build().action("room").path("room/home")

mainFragment = (BaseFragment) roomRes.data();

BRouterRes mineRes = BRouter.push(

getApplicationContext(),

BRouterReq.build().action("mine").path("/mine/info")

mineFragment = (BaseFragment) mineRes.data();

BRouterRes messageRes = BRouter.push(

getApplicationContext(),

BRouterReq.build().action("message").path("/message/info")

messageFragment = (BaseFragment) messageRes.data();

// fragment 展示

类似于获取其它模块的Fragment,

BRouterRes roomRes = BRouter.push(

getApplicationContext(),

BRouterReq.build().action("room").path("room/home")

mainFragment = (BaseFragment) roomRes.data();

BRouterRes res = BRouter.push(

getContext(),

BRouterReq.build().action("repair").path("repair/main/clazz"));

Intent intent = new Intent(getActivity(), (Class) res.data());

intent.putExtra("COMMAD", "func");

getActivity().startActivityForResult(intent, REQUEST_CODE);

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

组件化开发基本架构到此已有雏形,可以进行简单的业务功能开发。