大规模项目完全 gradle 化的一次落地总结,移动互联网开发专业

if (!productFlavors) {

productFlavors.add(’’)

}

productFlavors.each { productFlavorName ->

buildTypes.each { buildTypeName ->

def flavorNameCapitalized = “${productFlavorName.capitalize()}”

def buildNameCapitalized = “${buildTypeName.capitalize()}”

def targetName = “ f l a v o r N a m e C a p i t a l i z e d {flavorNameCapitalized} flavorNameCapitalized{buildNameCapitalized}”

def ndkCmdBuildTaskName = “compile${targetName}NdkBuildCmd”

def ndkCmdBuildTask = tasks.create(name: ndkCmdBuildTaskName, type: Exec) {

group = “other”

description = “ndk build with cmd style for ${targetName}.”

doFirst {

println("${project.name} start ndk build use cmd…")

def ndkDir = System.getenv(“ANDROID_NDK_HOME”)

if (ndkDir == null) {

Properties properties = new Properties()

properties.load(new FileReader("$rootDir/local.properties"))

ndkDir = properties.getProperty(“ndk.dir”)

}

println("#command is# ${ndkDir}/ndk-build ${ndkBuildCmdArgs}")

if (ndkDir != null) {

if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {

commandLine("${ndkDir}\ndk-build.cmd", *ndkBuildCmdArgs)

} else {

commandLine("${ndkDir}/ndk-build", *ndkBuildCmdArgs)

}

} else {

commandLine “echo”, “ndk path is null, please check you environment(example: export ANDROID_NDK_HOME=/opt/android-ndk-r10d, the key must named as ANDROID_NDK_HOME)!”

}

}

if (ndkBuildCmdArgs.size() == 0) {

enabled = false

}

}

//runBefore(“pre${targetName}Build”, ndkCmdBuildTask)

runBefore(“compile${targetName}Ndk”, ndkCmdBuildTask)

//按照源码里几个 task 都处理下!

mustRunAfter(“compile${targetName}Renderscript”, ndkCmdBuildTask)

mustRunAfter(“merge${targetName}JniLibFolders”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithIntermediateJniLibsFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithMergeJniLibsFor${targetName}AndroidTest”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithMergeJniLibsFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithStripDebugSymbolFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithSyncJniLibsFor${targetName}”, ndkCmdBuildTask)

}

}

def ndkCmdBuildCleanTask = tasks.create(name: “ndkCmdBuildClean”, type: Delete) {

group = “build”

description = “clean the build of ndk build with cmd.”

def ndkBuildCmdClean = project.hasProperty(“ndkBuildCmdClean”) ? project.ndkBuildCmdClean : []

if (ndkBuildCmdClean.size() == 0) {

enabled = false

}

doFirst {

delete ndkBuildCmdClean

}

}

runBefore(“clean”, ndkCmdBuildCleanTask)

}

如上实现了一个全新的构建 task,叫做compile${targetName}NdkBuildCmd,so 的构建都通过他来处理,需要构建项目如下配置即可:

apply from: ‘…/…/build_common.gradle’

apply from: ‘…/…/ndk_cmd_build.gradle’

ext {

ndkBuildCmdArgs = [’-C’, file(‘jni’).absolutePath, ‘-j’, Runtime.runtime.availableProcessors(), ‘NDK_DEBUG=0’]

ndkBuildCmdClean = [file(‘libs’), file(‘obj’)]

}

android {

sourceSets {

main {

jniLibs.srcDir ‘libs’

java.srcDirs = [‘src’, ‘androidImpl’]

}

}

}

这样在构建时就能看到 so 了,也就解决这个问题。还是要升级 gradle 就美了,就能迁移到官方标准参数了,因为高版本 gradle 已经完美支持参数配置了。

多 dex 爆炸问题

我们的 dex 分包是在主项目合并后的结果中依据包目录结构和 jar 包路径进行划分剪切到对应 dex 目录实现的,原来 ant 构建很多路径在现有的多 dex 插件分包规则中已经不匹配,所以一上来构建就炸了,按照规则重新适配即可解决。

apk 大小对比分析及 dex 打错问题

一切构建都通过了,接下来就是二次确认,所以确认操作就是通过我们多 dex 构建的 log 去排查有没有打串或者打错 dex 目录结构的,确认后进行微调即可。

apk 大小是通过 AS 的分析工具进行处理的,简单来说就是对比即可,首先构建一个 ant 包,接着构建一个同一基线的 gradle 包,然后通过 AS 分析工具的 compare 进行对比生成差异清单,看下多打了什么,然后删除多打的东西即可。

定位 apk 多打入莫名其妙东西和少打入一些东西的问题

通过 gradle 构建后的包确实多了一些东西,譬如 xml、assert、okhttp 的一些文件等。这些文件需要处理掉,如下:

android {

//打包时不打入assert目录下的指定资源

aaptOptions {

//https://stackoverflow.com/questions/25910206/gradle-exclude-file-from-android-assets-folder

ignoreAssetsPattern “!xxxx”

}

//不打包的东西

packagingOptions {

exclude ‘xxx/xxx/*.xml’

//…

}

}

接着就是少打入东西的问题,原因其实就是原来 eclipse 项目,有一些配置文件放在项目的 src 目录下的,所以 gradle java 构建 jar 默认是不包含的,因此想让这些位于 src 下的配置文件打入最终 apk 的根目录就需要如下配置:

apply from: ‘…/build_java.gradle’

processResources {

from(‘src’) {

include ‘xxxx.json’

include ‘xxxx.types’

include ‘sdk-xxx.txt’

include ‘xxx-certs.crt’

}

}

如上方式打包出来的 apk 根目录下就自动有这些配置文件了。

启动 apk 加载 assert 下指定文件崩溃

这个问题是因为 aapt 在打包时会对 assert 目录下的一些非特定后缀文件进行压缩,所以 app 中直接 assert 读取时就会提示格式错误异常,解决这个问题也很容易,如下:

android {

//指定后缀文件不压缩

aaptOptions {

noCompress “tflite”

}

}

sourceSet 不覆盖问题

这个问题是因为 apply 了一些 common 的 gradle 文件,其中已经定义了 sourceSet,如果当前文件对 sourceSet 进行 include 新目录会导致原来 common 里面的目录失效,所以需要完全重写即可。

windows ndk 问题

这个问题十分坑爹,linux 构建完全没问题,windows 下构建 ndk 出问题,构建 log 看到的 path 总是没转义的,奇怪了很久,发下那是自己修改的问题,其实就是Android.mk中的路径结尾少加了字符串换行连接操作,如下:

//缩略样例

export include = $(path)/ \

Application.mkAPP_PLATFORM := android-14无效问题

这个问题是因为 ndk 构建失败才发现的,一开始还在纳闷,两种构建没区别啊!代码也一样啊,为啥这样就编译不过,后来看 log 才发现 gradle 读到的 APP_PLATFORM 是 1,所以认为 jni 代码里面很多有兼容问题,所以去看了下 gradle 插件代码,发现这玩意在 gradle 下已经不起作用了,赋值是读的 minSdkVersion 值,所以对该 module 的 gradle 设置 minSdkVersion 为 14 即可构建通过(这其实也是 ndk 的最小兼容特性,没读到就会有最低版本的 ndk 去尝试编译,所以失败了)。

总结


路漫漫其修远兮,吾将上下而求索。

很多人说为什么要这么去兼容,自己也不会遇到这些问题,为什么不用官方的,我只想说我也想,只是项目真的太大,很多事不是开发层面的问题,而是沟通协作的问题。

统归来说,收获的经验就是知道与做到的差距其实真的很大。上面总结的问题其实大多数自己都知道,但是配合上不熟悉且复杂的构建后,思维总是在掉链子。所以纯技术上的收获其实没什么,但是技术外的感悟却收获满满,真的是行胜于言,做一个行动派吧。

在这里插入图片描述

问题,而是沟通协作的问题。

统归来说,收获的经验就是知道与做到的差距其实真的很大。上面总结的问题其实大多数自己都知道,但是配合上不熟悉且复杂的构建后,思维总是在掉链子。所以纯技术上的收获其实没什么,但是技术外的感悟却收获满满,真的是行胜于言,做一个行动派吧。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值