Android渠道包方式实现一套代码差异化处理总结

690 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

前言

有时候我们在维护的一套代码时,可能会遇到在这个代码的基础上,修改部分功能再出一个新应用的需求。

新应用和老应用可能存在的差异点有:

  • 包名、应用名、签名、版本号、配置参数等
  • 各种资源(图片、颜色、字体、文案等)
  • 功能逻辑差异

尽管我们可以复制代码创建新工程,或者切一个新分支,但如果作为长期使用的方案,它们都不太合适的地方。那么就有了本文————通过渠道包的方式,实现一套代码包含所有差异化处理

gradle配置

配置渠道包

  1. 创建package

如下图所示,在主module的src下创建相应的package,作为不同的渠道,applebanana是渠道名称,相当于两个应用各自的路径,互相独立,apple不能访问banana的内容,除此之外的内容则是公用的

image.png
  1. 配置productFlavors

打开主module的build.gradle,在android下添加如下代码:

    productFlavors {
        flavorDimensions "name"

        apple {
            dimension "name"

            applicationId "com.mobile.apple"
            versionCode 10000
            versionName "1.0.0"
        }

        banana {
            dimension "name"

            applicationId "com.mobile.banana"
            versionCode 20000
            versionName "2.0.0"
        }
    }

同步代码,点击工程左侧的Build Variants,如果出现这些渠道参数,就表示创建成功了。之后我们想要打哪个包,就在这里选中对应的包参数即可

1671611106769-image.png

包名、版本号、配置参数,都可以在这里修改。

  1. 注意点 在gradle配置的 defaultConfig中,既存在和渠道 apple中重复的配置属性(如 applicationId,versionCode等),也存在不同的配置属性(如 minSdk等),最终,重复的属性会被渠道包中的值覆盖,不同的则会全部保留

资源

资源文件和gradle配置属性一样,渠道包中的图片、文案、清单文件、assets等,都可以覆盖公共路径下的同名内容,不同的内容则会被保留。

如图,main模块的ic_launcher.webpAndroidManifest.xmlapple模块的覆盖替换,而它的ic_launcher_round.webp得以保留。

替换字符串,如下图所示

1671615106061-image.png

每个渠道包只要在各自的路径下修改、替换相应的内容,即可实现名称、logo等差异

逻辑差异

通过渠道包+抽象类/接口的方式,可以较为优雅的控制某些功能和逻辑。话不多说,以抽象类为例,上代码:

//1.主module中创建,定义那些存在差异化处理逻辑的方法
//抽象类
abstract class OnFlavorsFunctionDiff {
    abstract fun getColor(): String
    abstract fun getShape()
    fun isFood(): Boolean = true
}

//或接口
interface OnFlavorsFunctionDiff {
    fun getColor(): String
    fun getShape()
}

//抽象类或接口任选其一都可以实现,不过个人认为抽象类相对接口更加灵活
//2.主module,设置和保存diff的静态工具类
object FlavorsFunctionDiff {
    private var mDiff: OnFlavorsFunctionDiff? = null

    fun setDiff(diff: OnFlavorsFunctionDiff) {
        mDiff = diff
    }

    fun getDiff(): OnFlavorsFunctionDiff {
        if (mDiff == null){
            throw RuntimeException("must init diff first")
        } else {
            return mDiff!!
        }
    }
}
//3.在Application中设置Diff,而此处的CustomFunctionDiff就是子类或者实现接口方法的类
class App: Application() {

    override fun onCreate() {
        super.onCreate()
        FlavorsFunctionDiff.setDiff(CustomFunctionDiff())
    }
}
//4.在各个渠道包路径下,继承或依赖OnFlavorsFunctionDiff,创建自定义的实现类,重载重写实现这些方法
class CustomFunctionDiff : OnFlavorsFunctionDiff {
    override fun getColor(): String {
        return "red"
    }

    override fun getShape() {
        Log.i("Diff", "shape is round")
    }
}
//5.最后在代码里根据这个,来决定走不同的逻辑
FlavorsFunctionDiff.getDiff().xxx

感谢阅读,欢迎交流指正~