Android组件化、模块化实现

前言

移动端平台不断发展,不断迭代更新,APP软件越来越复杂和庞大,维护和更新亦是如此。为了解决这些问题,降低软件的复杂性和耦合度,同时提高开发效率,模块化在移动端就变得势在必行。

模块化理解

模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程。每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。

通过以下类比可以更好地理解什么是模块化:

我们可以把软件看做是一辆汽车,开发一款软件的过程就是生产一辆汽车的过程。一辆汽车由车架、发动机、变数箱、车轮等一系列模块组成;同样,一款大型商业软件也是由各个不同的模块组成的。

汽车的这些模块是由不同的工厂生产的,一辆 BMW 的发动机可能是由位于德国的工厂生产的,它的自动变数箱可能是 Jatco(世界三大变速箱厂商之一)位于日本的工厂生产的,车轮可能是中国的工厂生产的,最后交给华晨宝马的工厂统一组装成一辆完整的汽车。这就类似于我们在软件工程领域里说的多团队并行开发,最后将各个团队开发的模块统一打包成我们可使用的 App 。

一款发动机、一款变数箱都不可能只应用于一个车型,比如同一款 Jatco 的 6AT 自动变速箱既可能被安装在 BMW 的车型上,也可能被安装在 Mazda 的车型上。这就如同软件开发领域里的模块重用。

到了冬天,特别是在北方我们可能需要开着车走雪路,为了安全起见往往我们会将汽车的公路胎升级为雪地胎;轮胎可以很轻易的更换,这就是我们在软件开发领域谈到的低耦合。一个模块的升级替换不会影响到其它模块,也不会受其它模块的限制;同时这也类似于我们在软件开发领域提到的可插拔。

模块化优缺点

优点

  • 架构灵活,焦点分离
  • 耦合低,模块间可自由组合、分解
  • 方便单个模块功能调试、升级、测试,提升开发效率
  • 多人协作只负责单独模块,互不干扰

缺点

  • 系统分层,调用链会很长
  • 模块间发送消息对比较损耗性能

模块化分层

组件模块区别定义

  • 组件:指的是单一的功能组件,地图组件、支付组件、分享组件等功能;
  • 模块:指的是独立的业务模块,如首页模块、聊天模块、播放模块等,模块相对于组件来说粒度更大。

整个项目分为四层,从下至上分别是:

(1)宿主层:不做具体的项目功能实现,只负责集成业务模块,组装成一个完整的APP;

(2)业务模块层:将项目的每个大功能模块拆分成的一个一个单独的module,可独立运行,同产品的不同项目也可复用;

(3)业务组件层:用于业务模块间调用,例如支付组件 、地图组件、分享组件等等;

(4)基础组件层:基础组件层,与业务无关,与项目也无关,所有项目都可以全部复用,包含了各种开源库以及和业务无关的各种自研工具库;

模块化分层.png

模块化分层,其实就是将业务模块层的各个功能业务拆分层独立的业务模块;进行模块化的第一步就是业务模块划分,划分的粒度需要根据项目情况进行合理把控,这就需要对业务和项目有较为透彻的理解。

模块化过程

1、每个单独的Module 都可以单独作为Application编译成 APK运行,同时也可以作为依赖包Library来整体编译打包。于是,需要在每个Module下的 build.gradle 中加入如下代码:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

isModule作为开关去控制是否为Application后者Library,只需要在项目gradle.properties文件中加入如下配置:

isModule=false

2、作为Application时清单文件需要设置Application属性及启动Activity等,而Library则简单很多,因此就需要两套不同的AndroidManifest.xml。同样,只要在每个Module下的 build.gradle的android配置中加入如下设置:

sourceSets {
    main {
        if (isModule.toBoolean()) {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            //release模式下排除debug文件夹中的所有Java文件
            java {
                exclude 'debug/**'
            }
        }
    }
}

保持原先拷贝一份AndroidManifest.xml到新建的debug目录下即可,如图所示:

image-20201209134330788.png

main目录下作为Library清单文件:

<application>
    <activity android:name=".BottomAlignmentActivity" />
</application>

debug目录下作为Application中清单文件:

<application
    android:icon="@drawable/ic_author"
    android:label="@string/advancedtextview_app_name"
    android:supportsRtl="true"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    <activity
        android:name=".BottomAlignmentActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

3、宿主APP在Application和Library模式下,进行不同依赖配置,:

if (!isModule.toBoolean()) {
    implementation project(path: ':advancedtextview')
}else{
    implementation project(":libbase")
}

4、模块间跳转

宿主工程中依赖的库是可以直接引用的,通过startActivity跳转,但组件之间不可以依赖。因此,当常规业务模块之间需要跳转引用,改如何处理呢?

  1. 隐式Intent,要跳转的活动在Manifest.xml中声明匹配规则,然后调用
Intent intent = new Intent(Intent.ACTION_VIEW, "<scheme>://<host>:<port>/<path>");
startActivity(intent);
  1. 利用反射
Class clazz=Class.fromName("com.nianlun.expample.MainActivity");
startActivity(this,clazz);
  1. 使用路由,这里推荐阿里的ARouter

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。

Arouter使用

  1. 添加依赖和配置

    android {
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                }
            }
        }
    }
    
    dependencies {
        api 'com.alibaba:arouter-api:x.x.x'
        annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
        ...
    }
    
  2. 添加注解

    // 在支持路由的页面上添加注解(必选)
    // 这里的路径需要注意的是至少需要有两级,/xx/xx
    @Route(path = "/test/activity")
    public class YourActivity extend Activity {
        ...
    }
    
  3. 初始化SDK

    if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
        ARouter.openLog();     // 打印日志
        ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
    }
    ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
    
  4. 发起路由操作

    // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
    ARouter.getInstance().build("/test/activity").navigation();
    
    // 2. 跳转并携带参数
    ARouter.getInstance().build("/test/1")
                .withLong("key1", 666L)
                .withString("key3", "888")
                .withObject("key4", new Test("Jack", "Rose"))
                .navigation();
    

更多进阶用法,可以跳转官方GitHub网站进行文档查看:https://github.com/alibaba/ARouter/blob/master/README_CN.md

问题解决

资源名冲突的问题,可以通过在 build.gradle 定义前缀的方式解决:

defaultConfig {
   ...
   resourcePrefix "module_name_"
   ...
}

参考资料:Android 模块化探索与实践

以上就是模块化的基本过程和实现,具体项目中我们还是要理清业务之间的关系,进行具体划分,提取公共依赖,剔除冗余代码,逐步进行重构。

借用以前的代码例子,模块化处理了一下,采用Arouter进行跳转,具体可以查看我的Github查看,地址如下

https://github.com/MickJson/DevelopmentRecord

欢迎点击查阅及Star,我也会继续补充其它有用的知识及例子在项目上。

欢迎点赞/评论,你们的赞同和鼓励是我写作的最大动力!

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