浅谈移动端模块化

一 模块化基础概念
二 为什么要进行模块化
三 模块化实例操作

话不多说,直奔主题,本文主要讲解我对模块化的理解,主要讲解以下问题:什么是模块化? 为什么要进行模块化?怎么进行模块化?

一 模块化基础概念

模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。
说白了就是划整为零的概念,模块化思想在我们生活中随处可见,盖楼,修桥,修路等等,比如修桥,现在都会在别的地方把桥面在别的地方造好,分成一块一块的,然后在一块组装。
其实很多概念都是先在传统行业出现,然后才被借鉴到软件工程领域,在于移动端,无论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(远程代理仓库)
这种类型的仓库,可以设置一个远程仓库的链接。当用户向 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,填好配置信息就好了
library的创建

工程,module我们都创建好了,我们来看下如何配置gradle任务上传module到nexus.
切换项目到project模式。
project下的项目结构

接下来是gradle配置我直接上图:
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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容