Android 页面代码粒度化管理进阶

距离上次发布的有关代码目录结构的文章 《掘金之路(八)页面代码粒度化管理》 已经快5年了,这些时间里我开发的项目一般都是基于这种目录结构,当然在此基础上也有所改进,这就是写这篇文章的原因了。

如果问我经历过的最复杂的项目是什么,我想应该是一个扫读笔项目,这个项目的复杂不在于功能上的难易,而是在于代码结构上的复杂。想象一下,这个项目有n多个客户,每个客户可能同时有几款不同型号的 Android 定制设备,有小方块屏的(类似手表屏幕大小),有长屏的(比如128*96等几个尺寸)、有宽屏的,Android 系统覆盖 Android 4.4 - Android 11,当然还会涉及到定制系统的许多调用差异(比如有些系统集成了扫描 OCR 能力,可以对外提供调用,有些没有,需要自己集成第三方 OCR 去实现),最后还有客户的部分定制化需求(比如界面或功能)。

问题来了,如果你负责开发维护这个项目,你怎么来搭建一个架构,要求满足以下3点:

1、代码目录结构要清晰,随着项目功能增长也能保持清晰,方便以后开发和维护;

2、能够最大化的复用相同代码,避免维护多份相同代码;

3、能够灵活的处理差异化需求代码

你可以先思考,不用着急,思考完再往下看。

思考中...

下面先贴上我的答案:PinDemoProject,当然你可能会有更好的答案,欢迎在评论里一起分享。

代码结构的配置逻辑在根目录的 build.gradle.kts,这里就不贴上全部配置代码了。接下来只简单介绍 flavor 的配置项信息及对应的效果,不会说到各个配置的具体实现,如果对实现有兴趣的话还是需要去看 build.gradle.kts 里的实现代码。

flavor 的配置项信息如下:

kotlin 复制代码
/**
 * flavor 参数配置:
 * applicationId:应用ID,没有配置则使用默认值
 * screenOrientation:屏幕方向
 * supportFocus:是否支持焦点(有物理方向按键或确认键等功能键则表示支持)
 * modules:指定要引入的业务模块以及差异化页面(pins-diff)
 *    - pinsVersion:引入 pins-${pinsVersion} 目录下的所有页面
 *    - diffPins:指定需要引入 pins-diff 目录下的差异化页面
 * versionCode:版本号
 * versionName:版本名称
 * abiFilters:指定保留的ABI类型
 * systemApiLevel:设备的系统版本
 * signingConfig:签名配置
 */
private val flavorConfigs = mapOf(
    "flavorA" to mapOf(
        "screenOrientation" to "landscape",
        "supportFocus" to false,
        "modules" to mapOf(
            "m_core" to emptyMap<String, Any>(),
            "m_user" to mapOf(
                //user 模块使用 v1 版本:
                "pinsVersion" to "v1",
                "diffPins" to emptyArray<String>()
            ),
        ),
        "versionCode" to 1,
        "versionName" to "1.0.0",
        "systemApiLevel" to 21,
    ),
    "flavorB" to mapOf(
        "applicationId" to "cbfg.pin.flavorB",
        "screenOrientation" to "landscape",
        "supportFocus" to false,
        "modules" to mapOf(
            "m_core" to emptyMap<String, Any>(),
            "m_user" to mapOf(
                //user 模块使用 v2 版本:
                "pinsVersion" to "v2",
                //引入 pins-diff 中的 p_vip 页:
                "diffPins" to arrayOf("p_vip")
            ),
        ),
        "versionCode" to 1,
        "versionName" to "1.0.0",
        "systemApiLevel" to 19,
        "signingConfig" to { appExtension: com.android.build.gradle.AppExtension ->
            appExtension.signingConfigs.create("xxx") {
                keyAlias = "xxx"
                keyPassword = "xxx"
                storeFile = file("xxx.jks")
                storePassword = "xxx"
            }
        }
    ),
    "flavorC" to mapOf(
        "applicationId" to "cbfg.pin.flavorC",
        "screenOrientation" to "portrait",
        "supportFocus" to true,
        "modules" to mapOf(
            "m_core" to emptyMap<String, Any>(),
            "m_user" to mapOf(
                //user 模块使用 v3 版本:
                "pinsVersion" to "v3",
                //引入 pins-diff 中的 p_vip 页:
                "diffPins" to arrayOf("p_vip")
            ),
        ),
        "versionCode" to 1,
        "versionName" to "1.0.0",
        "systemApiLevel" to 19,
    )
)

结合注释,上面的配置应该不难理解,每个 flavor 的配置都是独立的,关键的一个配置是 modules 配置,可以根据 flavor 需要去引入所需的模块及版本,同时可以额外去引入差异化目录中的页面(比如 flavorB 和 flavorC 的 p_vip 页是一致的,但是 flavorA 的是专属的,那么就把 flavorB 和 flavorC 的 p_vip 页提取到了差异化目录 pins-diff 中,flavorB 和 flavorC 额外去引入即可)

接下来再看下项目中 user 模块代码目录截图:

其中:

1、pins 目录是放置公用页面代码的,放置在这个目录内的页面代码默认会被引入;

2、pins-diff 是放置差异化页面代码的,需要在 flavor -> modules ->对应模块 -> diffPins 配置中加入才会被引入;

3、pins-v1、pins-v2、pins-v3 对应的是这个模块的3个不同版本,模拟不同 flavor 需要的不同版本,需要在 flavor -> modules -> 对应模块 -> pinsVersion 配置中指定才会引入对应的版本(pinsVersion 的命名可以是随意的,可以根据自己的需要去起名称,比如可以是 pins-flavorA,只要在 flavor 的配置中指定 pinsVersion 为 flavorA 即可);

从截图中还可以看到 pins 目录下有个 p_user_core 目录,pins-v3 目录下有个 p_user_ui 目录(其实 pins-v1、pins-v2 下也有 p_user_ui 这个目录),这里模拟的是3个版本中,p_user 页 ui 部分可能不同,但是核心逻辑是相同的,所以抽取了核心逻辑到 pins 目录下(对应 pins/p_user_core),而 ui 部分则保留在各自版本中(对应 pins-v1、pins-v2、pins-v3 目录里的 p_user_ui),这里只是为了说明配置能够灵活的复用公共代码并支持差异化。

介绍完毕,详细配置见:build.gradle.kts

相关推荐
Digitally7 小时前
66最佳红米手机数据擦除软件
android
xiayiye58 小时前
Android开发之fileprovider配置路径path详细说明
android·fileprovider·android path配置·fileprovider配置
MoSheng菜鸟8 小时前
React Native开发
android·react.js·ios
CYRUS_STUDIO10 小时前
深入内核交互:用 strace 看清 Android 每一个系统调用
android·操作系统·逆向
BD_Marathon10 小时前
【Flink】DataStream API:UDF和物理分区算子
android·大数据·flink
liang_jy10 小时前
Java volatile
android·java·面试
CYRUS_STUDIO10 小时前
别再手工写 Hook 了!Python + Frida 一网打尽 SO 层动态注册 JNI 调用
android·c++·逆向
初始化12 小时前
JavaFx:生成布局 ViewBinding,告别 @FXML 注解
java·kotlin
leon_teacher12 小时前
ArkUI核心功能组件使用
android·java·开发语言·javascript·harmonyos·鸿蒙