一 模块化基础概念
二 为什么要进行模块化
三 模块化实例操作
话不多说,直奔主题,本文主要讲解我对模块化的理解,主要讲解以下问题:什么是模块化? 为什么要进行模块化?怎么进行模块化?
一 模块化基础概念
模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。
说白了就是划整为零的概念,模块化思想在我们生活中随处可见,盖楼,修桥,修路等等,比如修桥,现在都会在别的地方把桥面在别的地方造好,分成一块一块的,然后在一块组装。
其实很多概念都是先在传统行业出现,然后才被借鉴到软件工程领域,在于移动端,无论Android,还是iOS,移动web等平台,都有模块化的方案。
1 模块化的有什么优点
1 可以将复杂的问题简单化。解决一个大问题难,但是解决一个一个小问题还是相对简单的,模块化就将一个整体分解成各个小问题。
2 可以并行开发,每个人负责不同的开发模块,同时开发,提高开发效率。
3 标准化的模块,可复用率也很强..等等。
2 移动端都有哪些模块化方案?
Android :对于中大型项目android studio提供了Module(即模块),主要便于项目分层以及分离逻辑,使项目逻辑更加清晰也便于项目维护,我们可以利用它来实现模块化开发。
iOS:我们可以利用CocoaPods(Swift和Objective-C语言中Cocoa项目中依赖的管理工具)来实现模块化开发。
web:可以利用npm包管理工具来实现模块化开发。
二 为什么要进行模块化
上面说了一下模块化概念有哪些优点,以及移动端这块一些模块化方案,但是想想为什么我们需要在移动端模块化呢,我先总结下我认为的原因吧,主要移动端有些问题需要模块化来进行解决.
1 移动端开发所遇到的问题?
1 在开发多款App的时候,一些基础库像网络操作,工具辅助类,我们每开一个新项目,都要复制一份,工作量大还容易出错,后期还不容易管理。
2 项目随着不断的迭代,文件越来越多,层次越来混乱,项目越来难以迭代。
总结来说就是:复用率低,重复劳动多,项目越来越臃肿,层次不清晰,越来越难以迭代。
2 那么模块化又能怎么解决我们这些问题呢?
1 使用了模块化,那么一些基础库,像网络操作,基础工具类,等等,我们可以打包成通用模块,不管开发多少个项目,我们直接引用就行了,方便 快捷 不易出错。
2 使用了模块化,不同的业务可以划分不同的模块 ,像登录模块,支付模块,分享模块,推送模块,等等,每个人负责不同模块,项目主体直接引用各个模块,主项目看起来很简洁,模块又划分很清楚,不会出现相互修改的现象。
3 那么模块化有没有什么缺点呢?
1 基础模块如果有变动,你需要考虑所有引用项目能否兼容。
2 模块间通信问题,比如支付模块跳转登录模块等(目前可以借助路由来实现)等
总之模块化不是万能的,要根据实际项目的需求来看需不需要模块化。
三 模块化实例操作
基础概念讲完了,那么模块化如何做呢,拿一个之前工作遇到的问题来讲一下吧,之前公司开发的App比较多,好几款,每次开发新项目,我们的做法都是复制一份,然后删除之前的逻辑代码,只留下基础库和大体的项目框架,感觉这样搞每次太麻烦了,索性把基础库搞成一个个模块,然后直接引用就OK了。下面我以android平台为例介绍一下,我们主要使用Android studio的module,和Nexus(私有Maven仓库)
我先说一下主体流程:安装Nexus搭建好maven私有库->Android studio创建主体工程->创建library(module)->gradle配置library上传module到Nexus->主体工程引用module->运行完成
一 安装Nexus
1 通过官网Nexus,注册好后就可以下载了,这个地方遇到一个问题,一直下载不下来,点击下载后没反应,后来墙了下就能下载下来了。
2 将下载的压缩包,解压,会看到如下目录:
3 进入bin目录执行
iqusongdeMac-mini:bin iqusong$ ./nexus start
Starting nexus
iqusongdeMac-mini:bin iqusong$
4 访问http://localhost:8081/如果看到如下页面,就代表启动成功了。
5 点击右上角sigin in 用户名:admin,密码:它会提示你到一个文件下去查看的,打开那个文件,复制登录就OK了。
这里对这几个仓库做一下简单介绍
proxy(远程代理仓库)
这种类型的仓库,可以设置一个远程仓库的链接。当用户向 proxy 类型仓库请求下载一个依赖构件时,就会先在自己的库里查找,如果找不到的话,就会从设置的远程仓库下载到自己的库里,然后返回给用户,相当于起到一个中转的作用。
例如 maven-central 用来存储从 Maven 中央仓库下载过的构件。
group (聚合仓库)
在 Maven 里没有这个概念,是 Nexus 特有的。目的是将多个仓库聚合,对用户暴露统一的地址,这样用户就不需要配置多个地址,只要统一配置 group 的地址就可以了。group 仓库的聚合成员可以在仓库设置中添加和移除。
例如 maven-public 是一个 group 类型的仓库,通过引用这个地址,可以访问组内成员仓库的所有构件。
hosted(宿主仓库)
我们自己的构件,上传的就是这样的仓库。目前 maven-releases 和 maven-snapshots 是 hosted 类型的仓库。我们可以上传到这两个仓库,也可以自己创建 hosted 仓库。
一个本地的 Nexus 私库搭建完毕,我们将基础module(library)(依赖包)上传上去。这里我们使用使用 Gradle 的集成的 Maven 插件上传。
2 创建工程,配置上传module
具体步骤我就不详细说了,开发安卓的必会的东西这里我创建了一个主体工程micaibasenetwork,以及三个libray(module的一种类型),大体结构如下图所示:
还是简单介绍下library的创建吧,创建好工程后右键选new->module->Android Library,填好配置信息就好了
工程,module我们都创建好了,我们来看下如何配置gradle任务上传module到nexus.
切换项目到project模式。
接下来是gradle配置我直接上图:
就是在每个library下新建两个文件,一个属性文件,一个任务描述文件,源码文末我会给出,这里我举一个library的配置简单讲解一下,这里我选择mcbasenetwork.我们主要看他这三个文件,gradle.properties,upload_nexus.gradle,build.gradle.
gradle.properties
GROUP= com.micai
VERSION_NAME=1.0.2-SNAPSHOT
TYPE = 1
POM_ARTIFACT_ID= mcbaseapplication
SNAPSHOT_REPOSITORY_URL=http://localhost:8081/repository/maven-snapshots/
RELEASE_REPOSITORY_URL=http://localhost:8081/repository/maven-releases/
NEXUS_USERNAME= admin
NEXUS_PASSWORD= dangqianmingyue
upoad_nexus.gradle文件要用到的几个属性的定义,主要定义了库的组名,版本,快照存储路径,正式版本路径,nexus登录时的用户名和密码。
这里有一点特别注意,如果你的存储路径是快照存储路径,那么版本名必须要加上-SNAPSHOT后缀,不加会报错。
upoad_nexus.gradle
apply plugin: 'maven'
def isReleaseBuild() {
return TYPE!=1 ? true : false
}
def getRepositoryUsername() {
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
}
//def getRepositoryUrl() {
// // return isReleaseBuild() ? RELEASE_REPOSITORY_URL : SNAPSHOT_REPOSITORY_URL
// return SNAPSHOT_REPOSITORY_URL
//}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
snapshotRepository(url: SNAPSHOT_REPOSITORY_URL) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
// repository(url:RELEASE_REPOSITORY_URL) {
// authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
// }
}
}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
//解决 JavaDoc 中文注释生成失败的问题
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
options.addStringOption('charSet', 'UTF-8')
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}
这里也要注意的是snapshotRepository,repository同时只能配置一个另一个要注释,同时存在也会报错。
这里讲一下release 和 snapshot 仓库的区别
release 仓库不能重复上传同一版本号,版本不能覆盖,只能迭代 ,发布正式版本的时候使用
snapshot 仓库允许版本覆盖。当我上传多次上传同一个版本到 snapshot 仓库,会自动在版本号上添加时间戳来区分,开发时使用。
build.gradle
apply plugin: 'com.android.library'
apply from: 'upload_nexus.gradle'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
主要就是apply from: 'upload_nexus.gradle',引入配置的任务其他的没变化。
Android studio 右侧gradle tasks 双击uploadArchives
上传成功之后打开nexus我们可以看到下图:
最后我们来看一下如何在主工程引入:
Project:micaibasenerwork build.gradle
buildscript {
repositories {
google()
jcenter()
maven { url 'http://172.16.0.17:8081/repository/maven-snapshots/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.micai:mcbasenetwork:1.0.0-SNAPSHOT'
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'http://172.16.0.17:8081/repository/maven-snapshots/' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
引入库所在的路径
app:module build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "caicai.iqusong.com.mcmain"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.micai:mcbasenetwork:1.0.0-SNAPSHOT'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
ok 到此Android模块化演示就完成了。今后再有新的项目,直接像上面引入就行了,再也不用复制粘贴了。
这里仅仅演示Android的模块化,iOS平台也差不多,流程大致就是,搭建cocopod私有库,上传模块到私有库,主工程profile配置文件引入工程。这里就不详细演示了,web也一样。
最后我们可以发现,无论是那种平台,虽然使用的技术不一样,但是他们的流程思想都是一样的。软件开发中处处充满着这种统一思想,像设计模式,我们觉得我们应该着重培养软件设计能力,而不是软件技术能力,技术只能解决特定问题,思想才能解决绝大问题,就讲到这把,最后我把示例贴出来。
示例地址:https://github.com/lerpo/android-maven-module.git
nexus软件我也给一份:https://pan.baidu.com/s/1_X204SfAKOEj0AJr9B7UtQ