Android模块化开发架构

article/2025/10/22 10:05:22

模块化开发架构

为什么使用模块化架构开发方式

  在项目组开发过程中,一个项目业务有很多,每个成员都负责一部分业务开发,这个时候业务代码全部都糅杂在一起,不管是后面维护和代码阅读,都是很困难的,这个时候我们就想到使用模块化方式开发,也就是把每一块的业务独立出来开发,每个人负责自己的模块,分工明确、工程结构清晰、互不干扰…

思想:

  我们使用生产环境来控制我们的模块之间的关系,测试环境每个模块独立运行,让开发团队中成员实现模块独立开发以及独立调试以达到模块间互不干扰。

1、创建工程和业务模块,这里举例使用app-messageapp-settingapp-video三个业务模块来举例在这里插入图片描述

2、在工程的根目录创建env.gradle文件,然后在env.gradle文件中定义一个变量isRelease来控制当前所属环境。

ext {isRelease = true
}

3、在工程根目录的build.gradle中引入env.gradle文件,这样以来我们就能在各个模块的gradle文件中使用env.gradle里面的信息了

plugins {id 'com.android.application' version '7.4.2' apply falseid 'com.android.library' version '7.4.2' apply falseid 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}
apply from: 'env.gradle'

4、有了控制环境的isRelease之后我们来说如何使用,项目团队开发中,自己负责自己的业务模块,这个时候需要模块独立运行,所以这个时候app-message``app-setting``app-videoapplication;上线打包的时候业务模块就不需要独立运行,而是交给app模块调度,这个时候app-message``app-setting``app-video就是library了;由此我们只需要通过判断isRelease来修改模块的build.gradle就行了,代码如下:

plugins.apply('org.jetbrains.kotlin.android')if (isRelease) {plugins.apply('com.android.library')
} else {plugins.apply('com.android.application')
}android {...}dependencies {...}

每个业务模块都配置好了上面的代码之后,我们去修改env.gradle文件中的isRelease属性后点击sync来看看工程结构的效果吧;

5、因为项目中有多个模块,每个模块的build.gradle文件中都有相同的dependencies包,便于管理,我们抽取相同的内容到env.gradle

ext {isRelease = trueandroidx = [ktx             : 'androidx.core:core-ktx:1.7.0',appcompat       : 'androidx.appcompat:appcompat:1.4.1',constraintlayout: 'androidx.constraintlayout:constraintlayout:2.1.3']google = [material: 'com.google.android.material:material:1.5.0']test = [junit   : 'junit:junit:4.13.2',ext     : 'androidx.test.ext:junit:1.1.3',espresso: 'androidx.test.espresso:espresso-core:3.4.0']
}

然后我们在每个模块中的build.gradle中这样使用即可

dependencies {implementation androidx.ktximplementation androidx.appcompatimplementation google.materialimplementation androidx.constraintlayouttestImplementation test.junitandroidTestImplementation test.extandroidTestImplementation test.espresso
}

这样做的优点是所有模块引入包的版本号可以统一,能避免很多问题,也算是把第三方包集中管理,避免混乱等。

6、上面我们抽取了dependencies,但是我们发现每个模块中还是有很多重复的东西,我们把这些重复的东西抽取出来,我们创建app.gradle文件来抽取app-messageapp-settingapp-video模块中相同的gradle内容。

plugins.apply('org.jetbrains.kotlin.android')if (isRelease) {plugins.apply('com.android.library')
} else {plugins.apply('com.android.application')
}android {compileSdk 33defaultConfig {minSdk 24targetSdk 33testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}dependencies {implementation androidx.ktximplementation androidx.appcompatimplementation google.materialimplementation androidx.constraintlayouttestImplementation test.junitandroidTestImplementation test.extandroidTestImplementation test.espresso
}

7、抽取了每个模块build.gradle相同的代码之后,我们修改每个模块的build.gradle内容,引用我们刚刚创建的app.gradle文件:

app-messagebuild.gradle文件修改后就是这样的了,是不是很简洁了;还有app-settingapp-videobuild.gradle文件也是如此,这里就不一一展示了。

apply from: '../app.gradle'android {namespace 'com.ts.message'defaultConfig {if (!isRelease) {applicationId "com.ts.message"versionCode 1versionName "1.0"}}
}dependencies {}

至此,我们的模块化工程配置完毕。

部分建议和实战技巧

1、模块化开发的过程中,资源文件可能有重名的情况,这个时候我们在模块的build.gradle中配置一下资源文件名称规则,用来提示开发者命名规则,每个模块的资源文件按照规则来命名,就会避免资源打包合并出现的若干问题等。

android {namespace 'xxx'// 资源文件命名规范,建议:模块名称当前缀resourcePrefix 'message_'defaultConfig {...}
}

2、模块独立运行和合并打包的时候,AndroidManifest.xml文件还是有不同点的,这个时候我们区分一下环境来使用不同的AndroidManifest.xml,否则多个模块的AndroidManifest.xml文件在合并的时候会出问题,比如:

android:name=".xxxApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/xxx_app_name"
android:supportsRtl="true"
android:theme="@style/xxxTheme"

这些东西在多个AndroidManifest.xml合并的时候会出现冲突,所以我们配置不同的环境使用不同的AndroidManifest.xml

android {namespace 'com.xx.xxx'// 资源文件命名规范,建议:模块名称当前缀,比如message模块resourcePrefix 'message_'defaultConfig {...}// 配置资源文件sourceSets {main {if (isRelease) {manifest.srcFile 'src/main/AndroidManifest.xml'// 过滤掉任意路径下面的debug文件java.exclude '**/debug/**'} else {manifest.srcFile 'src/main/DebugAndroidManifest.xml'}}}
}

下面我们看看app-message模块中的AndroidManifest.xmlDebugAndroidManifest.xml文件内容

AndroidManifest.xml文件内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><application><activityandroid:name=".MessageActivity"android:exported="true" /></application></manifest>

DebugAndroidManifest.xml文件内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:name=".debug.DebugMessageApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/message_app_name"android:supportsRtl="true"android:theme="@style/MessageTheme.ModularArchitecture"><activityandroid:name=".MessageActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

3、模块的第三方SDK初始化问题:

每个业务模块都有自己的业务逻辑,每个模块都有可能会初始化一些SDK等,这个时候正式版的初始化操作和测试版的初始化操作我们怎么做?

思想是使用一个类来管理所有的初始化操作,模块使用反射机制来初始化SDK,在lib-base中创建ModuleMediator类,定义一个接口AppInitial来初始化SDK

private const val TAG = "ModuleMediator"/*** @author tu_shuai* date  : 2023/4/2 12:41* desc  : 模块中介,用来模块之间的交互,每个模块的Application都来实现ModuleMediator.AppInitial这个接口初始化SDK等*/
object ModuleMediator {/*** 模块的程序入口*/const val CLASS_NAME_MESSAGE = "com.ts.message.MessageActivity"const val CLASS_NAME_SETTING = "com.ts.message.SettingActivity"const val CLASS_NAME_VIDEO = "com.ts.message.VideoActivity"/*** 模块的Application*/private const val APP_MESSAGE_CLASS = "com.ts.message.MessageApplication"private const val APP_SETTING_CLASS = "com.ts.setting.SettingApplication"private const val APP_VIDEO_CLASS = "com.ts.video.VideoApplication"/*** 每一个模块的Application都实现此接口*/interface AppInitial {fun initSDK(app: Application)}/*** 通过反射调用initSDK来初始化SDK操作*/fun initSDK(app: Application) {val appClasses = arrayOf(APP_MESSAGE_CLASS, APP_SETTING_CLASS, APP_VIDEO_CLASS)for (claName in appClasses) {try {val clz = Class.forName(claName)val obj = clz.newInstance() as AppInitialobj.initSDK(app)} catch (e: ClassNotFoundException) {Log.e(TAG, "initSDK: Initialization failed, unable to find -> ${e.message}")}}}}

每个模块都创建一个类来继承ModuleMediator.AppInitial接口,在appapplication中来调用ModuleMediator.initSDK()来初始化每个模块的SDK,我使用app-message模块来举例:

class MessageApplication : ModuleMediator.AppInitial {override fun initSDK(app: Application) {Log.i("TAG", "initSDK: MessageApplication")}}
class App : Application() {override fun onCreate() {super.onCreate()ModuleMediator.initSDK(this)}}

这样就实现了多模块的SDK初始化操作,如果是独立运行模块的话我们怎么操作呢?

我们在app-message模块中新建一个DebugMessageApplication来调用MessageApplication.initSDK()即可

class DebugMessageApplication : Application() {override fun onCreate() {super.onCreate()MessageApplication().initSDK(this)}}

DebugAndroidManifest.xml中配置DebugMessageApplication就可以在独立运行的时候初始化SDK了。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:name=".debug.DebugMessageApplication"...></application></manifest>

至此,你就可以开始愉快的开发之旅了…
最后,奉上源码,结合源码理解更快 ModularArchitecture


http://chatgpt.dhexx.cn/article/Il17J1lr.shtml

相关文章

Vue的模块化开发

一、为什么需要模块化 客户端需要完成的事情越来越多&#xff0c;代码量也是与日俱增。 为了应对代码量的剧增&#xff0c;我们通常会将代码组织在多个js文件中&#xff0c;进行维护。 但是这种维护方式&#xff0c;依然不能避免一些灾难性的问题。 比如小明和小丽在不同的JS文…

小程序模块化开发

文章目录 1. 模块模块的定义和使用&#xff1a;注意 2. 模板模板的定义和使用&#xff1a;定义 使用注意 1. 模块 模块的定义和使用&#xff1a; module.exports {welcome: welcome }var welcome require(../utils/welcome.js) Page({data: {},onLoad: function() {…} })…

Android 模块化开发

概述 单独开发每个模块&#xff0c;用集成的方式把他们组合起来&#xff0c;拼出一个app。如通用的模块&#xff0c;自动更新的模块&#xff0c;反馈模块&#xff0c;推送模块都可以单独以模块来开发&#xff0c;最后进行集成。我们可以通过一个壳来包含很多个模块。 好处 可…

什么是前端模块化?前端模块化开发到底有无必要

转自:http://www.jianshu.com/p/e422c28e2471 序&#xff1a; 所谓前端开发&#xff0c;就是前台&#xff0c;常见的包括几个端&#xff1a;PC、pad、手机、其他智能设备&#xff0c;可以跑浏览器的地方就是我们前端人大施拳脚的乐土。自从node的问世&#xff0c;现在不光可以在…

什么是模块化开发?

什么是模块化开发&#xff1f; 前端开发中&#xff0c;起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果&#xff0c;后来js得到重视&#xff0c;应用也广泛起来了&#xff0c;jQuery&#xff0c;Ajax&#xff0c;Node.js&#xff0c;MVC&#xff0c;MVVM等…

深入理解JavaScript模块化开发

前言&#xff1a; 随着JavaScript应用程序的复杂性不断增加&#xff0c;模块化开发成为了一种必备的技术。通过将代码划分为模块&#xff0c;我们可以提高代码的可维护性、可重用性和可扩展性。在本文中&#xff0c;我们将深入探讨JavaScript模块化开发的概念、优势和不同的模块…

细说前端模块化开发

一、模块化概述 模块化开发是当下最重要的前端开发范式之一。随着前端应用的日益复杂&#xff0c;我们的项目代码已经逐渐膨胀到了不得不花大量时间去管理的程度了。模块化就是一种最主流的代码组织方式&#xff0c;它通过把我们的复杂代码按照功能的不同&#xff0c;划分为不…

模块化开发简述

模块化开发简述 都说模块化开发为前端发展带来了巨大的进步&#xff0c;然而不熟悉的人看着也是两眼一懵&#xff0c;那其实这到底是什么&#xff1f;好处在哪&#xff1f;我来说说自己的见解吧。 1. 模块化和传统开发的区别 实话讲&#xff0c;其实在我看来&#xff0c;两…

什么是模块化?模块化怎么实现?

什么是模块化&#xff1f;模块化怎么实现&#xff1f; 前言 &#xff1a; 增加印象&#xff0c;留下脚印 &#xff0c;忘记还可以翻一翻 奥利给。 1&#xff0c;什么是模块化 公司里一个项目是有很多程序员一起开发的&#xff0c;例如 “多人运动” 这个项目 有程序员a &…

vue 模块化开发

1、npm install webpack -g 全局安装 webpack 2、npm install -g vue/cli-init 全局安装 vue 脚手架 3、初始化 vue 项目&#xff1b; vue init webpack appname&#xff1a;vue 脚手架使用 webpack 模板初始化一个 appname 项目 4、启动 vue 项目&#xff1b; 项目的 p…

Android模块化开发

模块化开发项目搭建 1.为什么要模块化开发 随着APP版本不断的迭代&#xff0c;新功能的不断增加&#xff0c;业务也会变的越来越复杂&#xff0c;APP业务模块的数量有可能还会继续增加&#xff0c;而且每个模块的代码也变的越来越多&#xff0c;这样发展下去单一工程下的APP架…

vue模块化开发

1.前端代码化雏形和CommonJS JavaScript原始功能 在网页开发的早期&#xff0c;js制作作为一种脚本语言&#xff0c;做一些简单的表单验证或者动画实现&#xff0c;代码量比较少&#xff0c;只要写在script标签里面就可以了 随着ajax异步请求的出现&#xff0c;慢慢形成了前…

模块化编程

1.一般编程方式&#xff1a;所有函数放在“.c”文件里。 &#xff08;缺点&#xff1a;若使用的模块功能比较多&#xff0c; 则一个文件内会有很多的代码&#xff0c; 不…

一次跟你说清楚,什么是组件化开发?什么是模块化开发?

网上有许多讲组件化开发、模块化开发的文章&#xff0c;但大家一般都是将这两个概念混为一谈的&#xff0c;并没有加以区分。而且实际上许多人对于组件、模块的区别也不甚明了&#xff0c;甚至于许多博客文章专门解说这几个概念都有些谬误。 想分清这两个概念我觉得结合一下软件…

前端模块化开发

前端模块化开发 什么是模块化&#xff1f; 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元 编程领域中的模块化&#xff0c;就是遵守固定的规则&#xff0c;把一个大文件拆成…

模块化开发

模块化开发 1. 模块化开发最终的目的是将程序划分成 一个个小的结构 。 2. 这个结构中编写属于 自己的逻辑代码 &#xff0c;有 自己的作用域 &#xff0c;定义变量名词时不会影响到其他的结构。 3. 这个结构可以将自己希望暴露的 变量、函数、对象等导出 给其结构使用&#xf…

HttpClient CloseableHttpClient GetMethod PostMethod http

pom依赖 <!--HttpClient的依赖--><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><!--CloseableHttpClient的依赖--><de…

JAVA小工具-05-HttpClient/PostMethod上传文件(解决中文文件名乱码问题)

言于头:本节讨论的是在项目中利用HttpClient/PostMethod相关api进行上传文件操作时&#xff0c;会出现上传中文文件名乱码问题。为解决这个问题&#xff0c;下面是总结的一个HTTP工具类以及测试用例。 public class HttpUtils {public static final String UTF_8 "UTF-8&…

解决PostMethod的中文乱码

解决HttpClient的PostMethod的中文乱码问题 问题场景&#xff1a; 解决代码&#xff1a; 请求时设定编码格式&#xff1a; post.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8"); 完整代码&#xff1a; /*** 封装请求参数&#xff0…

php 取整,PHP取整的方法有哪些

本篇文章主要给大家介绍PHP取整的四种方法。 PHP实现取整的问题&#xff0c;不仅在我们学习PHP过程中会遇到&#xff0c;在我们PHP面试过程中也是常见的考点之一。 下面我们结合简单的示例给大家总结介绍PHP取整的四种方法。 第一种方法&#xff1a;直接取整&#xff0c;舍弃小…