Android|模块化探索抽取 basic 简化子 module 冗余

2020-05-22 15:50:41 浏览数 (1)

第86次推文

LZ-Says

前行之路,慢慢摸索。

前言

Android 的历史中,各种各样的技术一波接着一波,模块化,插件化,组件化等等。可怜的我,还依然观望,停滞不前。

接着项目重构的机会,果断先从模块化入手,后续渐渐转移阵地,开搞。

简单画个小图,总是感觉不如意。

左边是一直以来项目结构,右边是现阶段打算搞定的初版,欢迎各位指点一二。

百科 - 模块化 了解

万事开头难,还好,现在网络资源较多,先简单的了解下基础:

一、概念

(Modular design)所谓的模块化设计,简单地说就是将产品的某些要素组合在一起,构成一个具有特定功能的子系统,将这个子系统作为通用性的模块与其他产品要素进行多种组合,构成新的系统,产生多种不同功能或相同功能、不同性能的系列产品。

二、定义

模块化设计是指在对一定范围内的不同功能或相同功能不同性能、不同规格的产品进行功能分析的基础上,划分并设计出一系列功能模块,通过模块的选择和组合可以构成不同的产品,以满足市场的不同需求的设计方法。

三、原则

力求以少量的模块组成尽可能多的产品,并在满足要求的基础上使产品精度高、性能稳定、结构简单、成本低廉,模块间的联系尽可能简单;

模块的系列化,其目的在于用有限的产品品种和规格来最大限度又经济合理地满足用户的要求。

大刀阔斧拆分

Pro

  • app
  • helper
  • network
  • weight
  • 。。。

大刀阔斧的改完,很正常的遇到些问题,例如:

  • Android 重构 | 统一管理 Gradle 依赖版本

Q:每个子 module 下都有些基本依赖,难道每个子 module 都要写一次?

好比如下这些东西,每个子 module 都有:

代码语言:javascript复制
dependencies {
    def androidDependencies = rootProject.ext.dependencies

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation androidDependencies.kotlinStdlibJdk7
    implementation androidDependencies.appcompat
    implementation androidDependencies.coreKtx
    testImplementation androidDependencies.junit
    androidTestImplementation androidDependencies.testJunit
    androidTestImplementation androidDependencies.testEspressoCore
}

突然想到某天鸡老大曾经说过,抽取 build 不就好了吗?

项目根目录下创建 basic_depend.gradle,将如上那些基本的内容转移此 gradle 中:

代码语言:javascript复制
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    def androidDependencies = rootProject.ext.dependencies

    implementation androidDependencies.kotlinStdlibJdk7
    implementation androidDependencies.appcompat
    implementation androidDependencies.coreKtx
    implementation androidDependencies.constraintlayout
    testImplementation androidDependencies.junit
    androidTestImplementation androidDependencies.testJunit
    androidTestImplementation androidDependencies.testEspressoCore
}

子 module 添加此 gradle 依赖,删除原有依赖:

代码语言:javascript复制
apply from: "../basic_depend.gradle"

后记

慢慢摸索,慢慢成长,追逐鸡老大~

2020-05-06 更新

昨天写完之后,反复对比主 module 以及子 module,发现还是有一些共同的东西可以提取封装,还是对鸡老大的指点并不是完全的吸收。

鸡老大曾经说过,避免冗余,尽量有效封装(PS:我瞎编的,原话真心记不清了。。。)

回顾上面对 gradle 抽取,仅仅是分离了基本的依赖项,而每个 module 对应的基本信息呢?

例如:

  • 包名
  • 本地编译 SDK 版本
  • Gradle 编译项目工具版本
  • 最低/最高兼容 Android 版本
  • 版本编号、版本信息
  • 常用基本设置等

每个 module 除了基本的依赖,必备信息不可少,难道要每个 module 都来一次?就我这脑袋,还是算了,突然某天忘了写(拷贝),真的很尴尬,自己又没办法喷。

想想哪儿些是 module 独特的?

  • 版本号?(这个其实没想好,到底是跟随主 module 版本号还是按照自身的来,有点纠结,界限不是很明确)

其它的个人认为都可以直接抽取出来。

接下来开启改造之路~

basic_depen.gradle 这个名称也不太合适,索性直接改名为 basic.gradle 吧。

1. basic 改造

鉴于上面已经进行了简要分析,这里直接附上改造后的代码:

代码语言:javascript复制
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {

    def androidRoot = rootProject.ext.android

    compileSdkVersion androidRoot.compileSdkVersion
    buildToolsVersion androidRoot.buildToolsVersion

    defaultConfig {

        minSdkVersion androidRoot.minSdkVersion
        targetSdkVersion androidRoot.targetSdkVersion
        versionCode androidRoot.versionCode
        versionName androidRoot.versionName

        // 仅保留中文资源
        resConfigs "zh"
        // 启用多 dex 文件
        multiDexEnabled true

        ndk {
            // 设置支持的SO库架构
            abiFilters "armeabi"
        }

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

    // 开启视图绑定
    viewBinding {
        enabled = true
    }
}

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

    def androidDependencies = rootProject.ext.dependencies

    implementation androidDependencies.kotlinStdlibJdk7
    implementation androidDependencies.appcompat
    implementation androidDependencies.coreKtx
    implementation androidDependencies.constraintlayout
    testImplementation androidDependencies.junit
    androidTestImplementation androidDependencies.testJunit
    androidTestImplementation androidDependencies.testEspressoCore
}

2. 主 module 引用调整

代码语言:javascript复制
apply plugin: 'com.android.application'
apply from: "../basic.gradle"
apply plugin: 'kotlin-kapt'

android {

    buildTypes {
        debug {
            // 关闭资源缩减
            shrinkResources false
            // 关闭代码缩减
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        release {
            // 启用资源缩减
            shrinkResources true
            // 启动代码缩减
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    defaultConfig {
        applicationId "Your Package Name"
    }

}

/**
 * implementation:不会向下传递,仅在当前 module 生效;api:向下传递,所依赖的 module 均可使用
 */
dependencies {
    // 常用三方依赖导入部分
    。。。
}

3. 子 module 调整

代码语言:javascript复制
apply plugin: 'com.android.library'
apply from: "../basic.gradle"

android {

    defaultConfig {
        consumerProguardFiles 'consumer-rules.pro'
    }

}

dependencies {
    // 第三方依赖引用
}

这里特别说一下子 module 混淆问题,尤其是 consumerProguardFiles 和 proguardFiles 区别:

proguardFiles:

  • 配置混淆规则文件,只有 minifyEnabled 设置为 true 的时候会使用这个参数,文件中需要申明哪些文件不被优化和混淆。

consumerProguardFiles:

  • 描述:这个属性只作用于我们创建的 library 中,包括我们以aar形式导入的 library ,或是直接创建的 library。它的作用是,负责该 library 被进行编译时的混淆规则,我们在主 App 的模块下则可以不用再管理各个 library 的混淆规则,会直接使用各个 library 的混淆规则文件。
  • 值得一提:这个属性 和 proguardFiles 的区别在于,consumerProguardFiles 会被主 App 模块作为混淆文件使用导入,而 proguardFiles 则不会。

子 module 混淆文件内容需要特别注意下:

  • 需要写在 consumer-rules.pro;

参考资料

  • 模块化设计
  • buildTypes——安卓gradle

异常汇总

1、ERROR: Resource shrinker cannot be used for libraries.

Android Lib 中不能使用 shrinkResources 资源缩减。

0 人点赞