Android|Android + Gradle 代码自动选择编译

需求背景:
因华为市场的邀请,我们App可以获取与其联运的机会,即在华为市场首页的推广页放我们App的下载入口。

前提:
但合作的前提是,我们不能接入其它的第三方支付,必须得使用华为的支付,并且在进行包检测的时候,也不能发现其它的支付依赖包。

可选方案:
1. 切出一个新的分支,单独维护提供安装渠道包 2. 依然维护一个分支,通过gradle脚本进行打包编译干预

方案可行性对比
1. 方案:的确可以快速解决这个问题,给华为单独提供渠道包,并且可以决定哪些应该打入到安装包中,哪些资源不可以打入到其中。而其缺点在于,以后的维护成本特别高, 现在只有一个市场需要这么做,随着业务的推进,我们将会面监着两个分支公共代码的合并更新的问题。再退一步,如果后续有第二个,第三个联运业务时,我们将面临多分支代码的维护问题,因此这个方案实际上是不可行的,后果不可想象。

2. 方案:单独维护一个版本,但是需要编写gradle 脚本用来干预打包以及编译的过程,暂时的实现会比较的复杂,时间成本高一些,但对于后期的维护,一劳永逸。

所以:
方案一 保留==方案二==
最终的输出保证
1. 测试那边可以拿到全功能的打包产出文件,一个功能都不能落下 2. 每一个渠道包,至少可以保证拿到至少三种测试环境: ==开发环境,仿真环境,线上环境== 3. 不同的渠道包,我们可以干预其编译过程,可以自定义依赖。 4. 保证产出的apk,除了一些日志收集,以及api domain不同,主要按测试与线上来区分,其它的任何都是要保持与release上线的一样。

Android的apk生成维度
大家知道,在apk产出构建的时候,有两个维度很重要 1. BuildType 2. Flavor

考虑到当前项目的历史原因
项目中存在仅项目为release的BuildType时,才会执行热修复以及埋点功能在编译期的代码注入。而其它自定义的BuildType时,产出的apk不带有这种功能。 所以为了保证产出,交付给测试的apk是全功能版本,BuildType这种路看来已经被堵死

api,以及一些服务器的domain这些都可以根据flavor来进行自动的区分编译,但是支付的代码块呢?依赖呢?
回到项目,这次的升级,改版,就拿支付宝来说吧,华为支付中带有自己的支付宝,而我们原来的项目中,也有自己的支付宝sdk,如何做到,在编译主版本时候,选择打入我们原有的支付宝依赖,而在华为渠道版本时候单独打入华为sdk的支付宝依赖?
当前的项目结构依赖如下
graph BT libdependency-->libbase libbase-->bizframework bizframework-->compModuleA bizframework-->compModuleB bizframework-->compModuleC bizframework-->app IPay-->bizframework IPayImplProxy-->IPay HuaweiPay-->IPayImplProxy NormalPay-->IPayImplProxy ali_sdk_1.1.3-->HuaweiPay ali_sdk_2.0.0-->NormalPay

IPay假如包含的方法:
/** 支付初始化,以及发起支付接口 **/ public interface IPay{ void init(Context context); void startPay(OrderBean orderInfo,IPayResultCallback callback); } /** * 支付回调接口 **/ public interface IPayResultCallback{ void onSuccess(PayResult info); void onError(int resultCode); } /** 支付代理类,上层调用 **/ public class IPayImplProxy implements IPay{ public void init(Context context){} public void startPay(OrderBean orderInfo,IPayResultCallback callback){} } /** *ali_sdk_1.1.3 依赖的实现方案 **/ public class HuaweiPay implements IPay{ public void init(Context context){} public void startPay(OrderBean orderInfo,IPayResultCallback callback){} }/** *ali_sdk_2.0.0 依赖的实现方案 **/ public class NormalPay implements IPay{ public void init(Context context){} public void startPay(OrderBean orderInfo,IPayResultCallback callback){} }

IPayImplProxy 这个代理类,在后面将会成为我们一个重要的部份。实际上对于上层的调用方来说,它们只关心的是我通过个代理,能初始化支付,发起支付,能知道支付的结果就行了。
回过头来,我们不得不介绍gradle上面的一个强大的功能,可以根据flavor来选择决定哪个文件夹下面的代码需添加到编译过程中。也就是sourceSet会因为flavor的变化而被自动的扩展。
我们正常的项目src目录如下
[图片上传失败...(image-7a776b-1542550505269)]
那么对于华为渠道来说,我们可以根据flavor,假设flavor的名称为huawei,于是我们可以如下方式添加华为渠道包的单独代码文件
[图片上传失败...(image-870e6b-1542550505269)]
对的,就是在src下面,创建我们的代码文件夹,区分我们的实现代码。同理也可以创建一个normal代码文件夹用来实现正常的大渠道包支付实现。这样不同渠道的支付代码做了隔离,打包时候根据flavor也进行了隔离。
但是,我们并没有解决sdk的依赖包的隔离,我们更想要的是不同的flavor也可以对依赖包进行选择,如:我们打huawei flavor时候,会选择ali_sdk_1.1.3;但是打normal时候,会选择ali_sdk_2.0.0进行依赖

这个怎么实现呢?那就是
1. app build.gradle 下面添加两个flavor ,huawei,normal 2. 通过修改bizframework下面的build.gradle

app build.gradle 下面添加两个flavor ,huawei,normal
android{ productFlavors{ huawei{} normal{} } }

通过修改bizframework下面的build.gradle
[图片上传失败...(image-feebb4-1542550505269)]
dependencies{ huaweiApi('com.alibaba.pay:core-pay:1.1.3') normalApi('com.alibaba.pay:core-pay:2.0.0') }

最终实现的结构即如下所示:
在bizframework src文件夹下面分别创建huawei,normal 两个存放隔离的代码文件夹其中
huawei实现如下
/** 支付代理类,上层调用 **/ public class IPayImplProxy implements IPay{ private HuaweiPay huaweiPay; public void init(Context context){ huaweiPay=new HuaweiPay(); huaweiPay.init(context); } public void startPay(OrderBean orderInfo,IPayResultCallback callback){ if(huaweiPay!=null){ huaweiPay.startPay(orderInfo,callback); }else{ //提示需要初始化 } } } /** *ali_sdk_1.1.3 依赖的实现方案 **/ public class HuaweiPay implements IPay{ public void init(Context context){} public void startPay(OrderBean orderInfo,IPayResultCallback callback){} }

normal实现如下
/** 支付代理类,上层调用 **/ public class IPayImplProxy implements IPay{ private NormalPay normalPay; public void init(Context context){ normalPay=new NormalPay(); normalPay.init(context); } public void startPay(OrderBean orderInfo,IPayResultCallback callback){ if(normalPay!=null){ normalPay.startPay(orderInfo,callback); }else{ //提示需要初始化 } } } /** *ali_sdk_2.0.0 依赖的实现方案 **/ public class NormalPay implements IPay{ public void init(Context context){} public void startPay(OrderBean orderInfo,IPayResultCallback callback){} }

Application 中初始化支付sdk大致代码如下
public class CoreApplication extends Application{ private IPayImplProxy payProxy; private void initPaySdk(){ payProxy=new IPayImplProxy(); payProxy.init(this); } }

【Android|Android + Gradle 代码自动选择编译】当你完成了上诉的流程构建,很高兴的告诉你,你实现了不同flavor的代码构建隔离,包括依赖包的隔离。
思考
1. 每新增一个flavor,我们还需要在src下面为其单独创建一份独立代码即使与当前normal文件夹下面代码一致 2. 如果这些差分代码需要调整,flavor较多情况,我们需要同时修改差分文件夹下面的好几份.(后续考虑解决) 3. gitbub 后续补充代码,今天尝试....

    推荐阅读