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 后续补充代码,今天尝试....
推荐阅读
- CVE-2020-16898|CVE-2020-16898 TCP/IP远程代码执行漏洞
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- 不废话,代码实践带你掌握|不废话,代码实践带你掌握 强缓存、协商缓存!
- 工具|后天就是七夕节,你准备好了吗(送上几个七夕代码,展示你技能的时候到了!)
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库