当模块化遇上多渠道

本文探讨了Android模块化开发中遇到的多渠道问题,指出传统方式存在的问题,并分享了作者在解决这一问题时经历的踩坑过程,包括尝试的解决方案以及最终实现全量和单个渠道包的自动化生成,实现manifestPlaceholders功能,从而消除模块化项目中的多渠道编译问题。
摘要由CSDN通过智能技术生成

本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发。

最近在研究Android模块化开发的一些东西, 网上大多数模块化的文章都是仅仅从一个demo的角度去看待的, 其实对于在真实项目中使用还有很多坑需要去踩, 今天就来聊聊我在模块化探索过程中遇到的众多坑中的一个-多渠道.

传统多渠道

说道多渠道, 其实大部分开发者都会在项目中使用到, 例如按照Google Play, Qihoo360… 等渠道分发, 那么我们可能这么写.

productFlavors {
  GooglePlay {}
  Qihoo360 {}
}

当我们需要根据不同渠道去编译不用的库文件的时候, 我们可能会这么写.

GooglePlayCompile (name: 'lib_google_play', ext: 'aar')
Qihoo360Compile (name: 'lib_qihoo_360', ext: 'aar')

这么写在普通的Android项目中一点毛病也没有, 那么当我们将项目模块化后呢? 这样的渠道信息还需要在每个模块中写一遍, 而且当我们增加渠道的时候还需要在每个模块中去添加渠道信息,添加渠道compile语句, 这个最终会是一个可怕的工作量. 而且, 当你的项目的build.gradle中出现大坨下面的代码的时候, 你是不是也会疯狂起来.

GooglePlayCompile (name: 'lib_google_play', ext: 'aar')
Qihoo360Compile (name: 'lib_qihoo_360', ext: 'aar')
...

GooglePlayCompile (name: 'biz-user_google_play', ext: 'aar')
Qihoo360Compile (name: 'biz-user_qihoo_360', ext: 'aar')
...

上面的类似biz-XXX的写法肯定不在少数, 因为, 当我们进行模块化开发的时候, 业务代码肯定是希望下沉的, 那样, 任何一个module都能使用. 好, 今天的重点不是讨论模块化, 关于模块化的东西我们暂且不谈.

当我看到上面的使用方式的时候, 我是不能接受的, 所以我一直在寻找合适的方式来区分渠道, 下面再来说说我在寻找这个方法过程中踩过的坑.

踩坑阶段

当我分析上面compile语句后, 我发现这些代码都是有规律的, 如果按照这个规律来讲, 这些代码都是重复的. 来看看下面两个库.

  1. lib_google_play
  2. lib_qihoo_360

其实这两个库都是对lib的封装, 不同的地方是渠道不同而已, 所以首先我想到的是要规范库文件的名称, 就像上面的lib一样, 库的名字必须是名称_渠道名的小写字母. 有了第一次突破, 下面就来思考如何简化了.

首先我想到的, 就是包装一下compile语句, 代码如下:

class Flavors {
    static String flavorType = ""

    static void onTaskEach(String task) {

        if(task.contains("GooglePlay")){
            flavorType = "GooglePlay"
        } else if (task.contains("Qihoo360")){
            flavorType = "Qihoo360"
        }
    }

    static void compile(DependencyHandler dh, String lib) {
        if (flavorType == null || flavorType == "") { return}
        dh.compile(name: "${lib}_${flavorType.toLowerCase()}", ext: 'aar')
    }
}

gradle.startParameter.getTaskNames().each { task ->
    if (task.startsWith(":app")) {
        Flavors.onTaskEach(task)
    }
}

dependencies {
  Flavors.compile(getDependencies(), 'biz-user')
}

这样的方式在我把Flavors这个类放在每个模块中的时候还是好用的, 但是这不是我的目的, 我的目的是它至少可以放到根项目的build.gradle中, 很显然, 我失败了, 原因是DependencyHandler这个玩意找不到.

既然这种方式不行, 那接下来我在yanbober提醒下, 很快的想到了第二种方式, 如果那么辅助类只给提供渠道信息呢? compile语句不做封装, Flavors提供一个current方法用来提供当前的渠道信息, 似乎这次不错, 大体代码是这样的.

// project的build.gradle
ext {
  flavors = new Flavors()
}
class Flavors {
    static String flavorType = ""

    def onTaskEach(String task) {
        if(task.contains("GooglePlay")){
            flavorType = "GooglePlay"
        } else if (task.contains("Qihoo360")){
            flavorType = "Qihoo360"
        }
    }

    def current(String lib) {
      if (flavorType == null || flavorType == "") { return lib}
      return ${lib}_${flavorType.toLowerCase()}
    }
}

// module的buld.gradle
gradle.startParameter.getTaskNames().each { task ->
    if (task.startsWith(":app")) {
        rootProject.ext.flavors.onTaskEach(task)
    }
}

dependencies {
  compile(name: rootProject.ext.flavors.current('biz-user'), ext: 'aar')
}

这种方式在大部分情况下是ok的, 当然我也兴奋了一小会, 但是在拿到同事电脑上用的时候, 死活就是报错, 后来我发现这种方式在我第一打开项目的时候flavorType还没拿到就进行compile了.

发现不行后, 我就忍痛删代码了. 既然这种方式走不通了, 那就退而求其次吧, 接下来我写了一个studio插件, 通过快捷键来生成分渠道的compile, 很方便, 至少还是解决了部分问题–人力问题, 但是还是不能解决’添加渠道后, 添加渠道compile语句’和代码丑这个问题. 这个插件在我们项目中现在还是保留着, 因为我们的项目中使用的网址是按照渠道分发的, 而且网络框架是Glin, 一个类似retrofit的东西, 所以网址必须是一个字面常量, 这样封装的constant必须按照按照渠道来打aar, 业务包也必须按照渠道来打包, 这一点在我分析后发现是不能逃避的, 所以这也给我定了方向, 接下来我的目标就是干掉UI层模块的多渠道.

最终实现

在又经过多种方案尝试后, 我有了一个疯狂的想法–自己模拟实现一下多渠道, 而且最终也被我实现了(要不然也不会有本文了…), 最后总结了一下, 很多时候我们的思路都被现有的大家都这么用的方式给束缚住了, 我们需要大胆的想象, 必要的时候可以打破这种束缚.

要自己去模拟多渠道, 那么需要解决的问题如下,

  1. 实现全量渠道包的生成.
  2. 实现单个渠道包的生成.
  3. 实现渠道的切换.
  4. 实现manifestPlaceholders功能

第一点是我们要实现的基础功能. 第一点如果能实现, 第二点就不是问题了. 第三点是为了直接run的时候能统一渠道而必须要实现的, 想一下, 如果测试同事拿着手机和数据线来找你跑一个最新版的时候, 你总不能打出所有包来再给人家安装吧. 第四点的需求没有那么强烈, 但是也有可能会用到, 所以我放到最后去实现的.

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亓斌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值