ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Cocos安卓ANT打包:环境版本咬合、签名多渠道与避坑

Cocos安卓ANT打包:环境版本咬合、签名多渠道与避坑

简介:面向 Cocos 游戏开发者的安卓打包 ANT 全套配置包,聚焦 Mac 环境下将 Cocos 项目编译、签名并输出可安装 APK 的完整流程,适合已具备基础开发经验、需要打通发布环节的开发者参考。包内共 1634 个文件,以 1525 个 html 文档、24 个 pom 与 24 个 jar 依赖、15 个 xsl 及若干 cmd、bat 脚本为主,涵盖 ANT 运行所需的类库、说明文档与命令行入口,压缩包约 9.12MB,解压后可直接对照目录结构使用。目前已有 441 人学习。内容围绕 ANT 安装与环境变量配置、project.android 目录下 build.xml 参数调整、keystore 签名信息填写、ant release 执行以及 build.properties 编译优化等环节展开,并给出编译报错、资源缺失等常见问题的排查思路。读者可据此理解 ANT 构建脚本的组织方式,快速完成签名配置与打包验证,减少发布环节反复试错的成本。

1. Cocos安卓打包为什么还有人用ANT:一个被Gradle掩盖的稳定路径

安卓打包这件事,在Cocos项目里经常被一句“用Gradle就行了”带过。但存量工程、渠道定制包、CI老脚本里,ANT仍然是一条更可控的路:产物路径固定、命令行参数少、签名流程透明,出了问题看build.xml行号就能定位。它不新,却适合接手老Cocos工程、需要按渠道批量出包、又不想重做整条构建链的人。ANT方案的核心不是“复古”,而是把JDK、SDK、NDK、ANT四件事的版本咬合关系摆到明面上,任何一个版本错位都会在打包时直接翻车。下面按环境、工程改造、签名渠道、避坑、验证五段拆开,尽量把能抄的参数和要看住的日志写清楚。

2. 环境搭建:JDK、SDK、NDK、ANT四件套的版本咬合

2.1 为什么ANT方案对JDK版本更挑剔

ANT本身只是个构建调度器,真正挑版本的是它调用的javac和Android SDK里的旧ant构建脚本。很多Cocos工程里的build.xml会引用SDK自带的tools/ant/build.xml,里面默认把source和target写成1.6。JDK 8还能认这个参数,JDK 11之后直接报“Source option 6 is no longer supported”,日志里会指向javac那一行,看起来像工程代码写错,实际上是JDK太新。

我一般会把JDK固定在8,并且只给这个ANT工程用,不跟系统里其他新项目混。ANT用1.9以上都行,关键是ANT_HOME指向解压目录,PATH里能直接敲ant。SDK这边不要只装最新platform,ANT工程通常依赖project.properties里的target=android-XX,缺了对应platform会在aapt阶段报资源找不到。build-tools也要保留一套旧版本,常见做法是留28~30之间的一个版本给ANT用,因为新build-tools的目录结构和旧ant脚本偶尔对不上。

下面这张表是排错时最常对照的咬合关系,不是绝对版本表,而是“哪一项对不上会先炸”:

组件建议状态对不上的典型报错先看哪里
JDK8,JAVA_HOME指向JDK根目录Source option 6 is no longer supportedjavac日志行
ANT1.9+,ANT_HOME可用ant: command not foundPATH顺序
SDK platform与project.properties的target一致Failed to resolve targetplatforms/android-XX
build-tools保留一套旧版本aapt not foundbuild-tools/目录名
NDK与工程Android.mk兼容APP_PLATFORM或toolchain错误NDK_ROOT与jni/

这张表的使用方式很直接:先ant -version确认ANT本身没问题,再java -version确认JDK是8,然后进工程目录跑ant -p看target能不能被解析。如果ant -p就失败,问题通常在环境变量或build.xml引用路径;如果能列出target但编译失败,再往JDK、SDK、NDK三层里找。不要一上来就改工程代码,ANT打包的失败大多数发生在环境层。

提示:JDK 8的安装目录不要带空格,Windows下尤其不要放在Program Files下面。ANT脚本里很多路径没有做引号包裹,空格会把一个参数拆成两个。

2.2 SDK/NDK目录与环境变量配置

环境变量这一层,Linux/macOS和Windows写法不同,但目标一样:让ant、java、ndk-build、aapt都能被找到。下面给的是bash写法,路径用虚构的/opt/dev/前缀,实际替换成自己的目录即可:

# JDK:必须指向JDK根目录,不是bin目录 export JAVA_HOME=/opt/dev/jdk8 export PATH=$JAVA_HOME/bin:$PATH # SDK:ANDROID_SDK_ROOT和ANDROID_HOME同时给,兼容旧脚本 export ANDROID_SDK_ROOT=/opt/dev/android-sdk export ANDROID_HOME=$ANDROID_SDK_ROOT export PATH=$ANDROID_SDK_ROOT/platform-tools:$PATH # NDK:Cocos的jni构建会读NDK_ROOT export NDK_ROOT=/opt/dev/android-ndk export PATH=$NDK_ROOT:$PATH # ANT:指向解压目录 export ANT_HOME=/opt/dev/ant export PATH=$ANT_HOME/bin:$PATH # 验证顺序:java -> ant -> ndk-build java -version ant -version ndk-build --version

这段配置的逻辑是先固定JDK,再把SDK和NDK挂进来,最后挂ANT。JAVA_HOME指向JDK根目录,是因为ANT和SDK脚本都会用$JAVA_HOME/bin/javac这种拼法;如果指到bin,会变成bin/bin/javac。ANDROID_SDK_ROOT和ANDROID_HOME同时给,是为了兼容不同年代的脚本,有的读前者,有的读后者。NDK_ROOT是Cocos的jni构建常用变量,ndk-build --version能跑通,说明NDK目录和可执行权限都没问题。

Windows下对应的是系统环境变量,PATH里要包含%JAVA_HOME%\bin、%ANT_HOME%\bin、%ANDROID_SDK_ROOT%\platform-tools。改完之后一定要新开一个终端,旧终端不会自动刷新环境变量。很多人改完变量直接在原窗口跑ant,看到还是旧版本,就以为配置没生效,其实是窗口缓存。

SDK目录里至少要有三块:platforms/android-XX、build-tools/XX.X.X、platform-tools/adb。ANT打包会先读project.properties里的target,再去platforms找对应android.jar;aapt从build-tools里找。如果build-tools只有最新版,而旧ant脚本按固定目录名找,就会报aapt not found。最稳妥的做法是保留一套与工程年代接近的build-tools,不要只留最新。

NDK这边,Cocos工程通常在jni/Android.mk里写APP_PLATFORM和APP_ABI。NDK版本过高时,旧Android.mk里的APP_STL或toolchain路径可能变化,报错会指向ndk-build内部脚本。遇到这种报错,先看jni/Application.mk里的APP_ABI是否包含armeabi-v7a,再看NDK版本是否比工程预期新太多。不要直接改NDK内部脚本,换一套匹配的NDK比改脚本省时间。

2.3 ANT的安装与build.xml结构

ANT安装本身很简单:下载binary包,解压,设ANT_HOME,把bin加进PATH。验证用ant -version,能输出版本号即可。真正要看的是工程里的build.xml,它决定了ant clean debug和ant release到底做了什么。

进到Cocos构建出来的android工程目录,常见路径是build/jsb-default/frameworks/runtime-src/proj.android,目录里会有build.xml、local.properties、project.properties、AndroidManifest.xml、jni/、res/、src/。先跑:

cd build/jsb-default/frameworks/runtime-src/proj.android ant -p

ant -p会列出当前build.xml里可用的target。正常能看到clean、debug、release、install、uninstall这些。如果只看到clean没有debug,说明build.xml没有正确引入SDK的ant构建脚本,或者local.properties里的sdk.dir没写对。ant -p还有一个用处:它会提前解析import,如果import路径错了,这里就会报,不用等到编译阶段。

Cocos的build.xml一般会做几件事:引入SDK的tools/ant/build.xml,设置source.dir、resource.dir、assets.dir,再定义-pre-build、-post-build这类hook。debug和release的真正逻辑在SDK的ant脚本里,工程里的build.xml只是把路径和属性喂进去。所以排错时不要只盯着工程build.xml,要看它import的那个文件是否存在。

下面这条命令用来确认build.xml引用的SDK路径是否真实存在,路径从local.properties里读:

# 打印sdk.dir,并检查目录是否存在 grep '^sdk.dir' local.properties SDK_DIR=$(grep '^sdk.dir' local.properties | cut -d'=' -f2) test -d "$SDK_DIR" && echo "SDK目录存在" || echo "SDK目录不存在"

这段脚本先读local.properties里的sdk.dir,再用test -d判断目录。cut -d'=' -f2只适合路径里没有等号的情况;如果Windows路径带盘符,比如D:/work/sdk,也能正常截出来。判断结果为“不存在”时,不要继续跑ant,先把路径改成真实SDK目录。local.properties是本地文件,不应该提交到版本库,团队里每个人自己配。

ant -p通过之后,先跑一次ant clean debug,不要直接上release。debug包能出来,说明JDK、SDK、ANT、工程结构四层都通了;release再出问题,范围就缩小到签名和混淆。这个顺序能省很多时间,也是我处理老工程时固定的第一步。

3. 工程改造:从Cocos构建目录到ANT可编译结构

3.1 Cocos构建产物里的android目录拆解

Cocos构建出来的android目录不是随便放的,每个文件在ANT打包里都有明确角色。先把这个目录拆开看,后面改配置才知道动哪里。下面这张表按“文件/目录 → 作用 → 是否常改”列:

文件或目录作用是否常改
build.xmlANT入口,import SDK构建脚本少改,除非加自定义target
local.properties本机SDK路径每台机器改
project.propertiestarget、build-tools、library引用常改target
AndroidManifest.xml包名、版本、权限、渠道meta-data常改
ant.propertiesrelease签名参数出release必改
jni/Android.mknative模块编译规则加so时改
jni/Application.mkABI、STL、平台版本常看
assets/Cocos资源与脚本构建时生成
libs/第三方jar或so按需放
res/图标、启动图、XML资源常改
src/Java源码与包目录改包名时同步

build.xml是入口,不要轻易重写。它里面通常有一行import file="${sdk.dir}/tools/ant/build.xml",这一行决定了debug和releasetarget从哪来。local.properties只放本机路径,换机器就要改。project.properties里的target=android-XX必须和SDK里platforms目录对上,sdk.buildtools=XX.X.X要和build-tools目录对上。AndroidManifest.xml里的package是Java包名,不是应用显示名;改它要同步改src/下的目录结构,否则R文件找不到。

jni/Application.mk里常见的是APP_ABI := armeabi-v7a arm64-v8a和APP_PLATFORM := android-XX。如果只出armeabi-v7a,新设备安装可能没问题,但部分应用市场会要求64位包;如果加了arm64-v8a,要确认Cocos的so和第三方so都有对应架构。缺架构的典型现象是安装成功但启动闪退,日志里报UnsatisfiedLinkError,说找不到某个so。这个问题在打包阶段不一定报,要到安装后才暴露,所以出包前要检查libs/和assets/里的so架构。

res/和assets/的边界也要分清:res/是Android资源,走aapt编译,文件名只能小写字母、数字、下划线;assets/是原样打包,Cocos的脚本和资源通常在这里。把中文名或大写文件名放进res/,aapt会直接报错,日志里会写非法资源名。这个坑在替换图标时最常见。

3.2 local.properties与project.properties怎么写

local.properties只写SDK路径,格式是key=value,不要加引号。Windows下用正斜杠或双反斜杠,单反斜杠会被当转义。下面是一个示例:

# local.properties:只放本机路径,不提交 sdk.dir=/opt/dev/android-sdk

如果路径是Windows的D:\work\android-sdk,写成sdk.dir=D:/work/android-sdk或sdk.dir=D:\\work\\android-sdk。写成sdk.dir=D:\work\android-sdk时,\w和\a可能被当转义,导致路径变成乱码。这个文件不需要ndk.dir,NDK走环境变量NDK_ROOT更稳;如果工程build.xml明确读ndk.dir,再补一行。

project.properties管的是编译目标,常用项如下:

# 编译目标,必须与SDK/platforms/android-XX一致 target=android-28 # 指定build-tools版本,必须与SDK/build-tools目录名一致 sdk.buildtools=28.0.3 # 如果有Android Library依赖,按序号写 # android.library.reference.1=../library-demo

target=android-28不是随便写的,它要和platforms/android-28目录对应。如果SDK里只有android-30,这里写28就会报Failed to resolve target。sdk.buildtools=28.0.3要和build-tools/28.0.3对应,目录名差一个小版本都会找不到aapt。这两个值改完,跑ant clean debug,如果还报target错误,就用ls $ANDROID_SDK_ROOT/platforms和ls $ANDROID_SDK_ROOT/build-tools核对目录名,不要凭记忆写。

project.properties里如果引用了library,路径是相对当前工程的。library工程也要有project.properties和AndroidManifest.xml,否则ANT会在合并资源时失败。老工程里常见的android.library.reference.1=../frameworks/cocos2d-x/cocos/platform/android/java就是这种写法。路径不对时,报错会指向resolve library,不会直接说路径错,要看它打印的绝对路径。

3.3 用ANT命令行完成debug/release打包

配置齐了之后,打包命令本身很短。日常先跑debug,确认工程能编译:

cd build/jsb-default/frameworks/runtime-src/proj.android # 清理旧产物再打debug包 ant clean debug # 查看产物 ls -lh bin/*.apk

ant clean debug会先删bin/和gen/,再走资源编译、Java编译、dex、打包、签名(debug签名)。产物通常在bin/下,文件名包含debug或-debug.apk。如果bin/没有apk,先看日志最后是不是BUILD SUCCESSFUL;如果是BUILD FAILED,往上找第一个error,不要从最后一行倒着看,最后一行经常只是“总失败”。

release包的命令是:

# 打release包,签名参数从ant.properties读取 ant clean release # 如果build.xml支持属性传入,可以覆盖版本号等 ant -DversionCode=102 -DversionName=1.0.2 clean release

ant clean release和debug的区别在于:release会走签名、可能走混淆、默认不插入debuggable。签名参数没配时,有的工程会产出-unsigned.apk,有的直接失败,取决于build.xml里的target。-DversionCode=102这种写法能不能生效,要看build.xml里有没有用${versionCode}接收;没有接收就不要传,传了也不会改Manifest。想确认某个属性是否生效,可以在build.xml里临时加一行<echo message="versionCode=${versionCode}"/>,跑一次看输出。

出包后不要只看文件存在,先跑一次aapt dump badging看包名和版本号,具体命令放在最后一章。ANT打包的产物名有时带渠道后缀,有时是固定名,复制到发布目录时按package和versionCode重命名,不要按时间戳,否则渠道多了容易混。

注意:ant clean会删掉bin/和gen/,如果工程里有手动放进bin/的so或jar,会被一起删掉。第三方文件放libs/,不要放bin/。

4. 签名与渠道:release包、keystore和AndroidManifest的联动

4.1 keystore生成与ant.properties签名参数

release包必须签名,ANT读签名的位置通常是工程根目录的ant.properties。先准备keystore,用JDK自带的keytool生成:

# 生成release keystore,有效期按需调整 keytool -genkeypair -v \ -keystore release.keystore \ -alias demoalias \ -keyalg RSA \ -keysize 2048 \ -validity 36500 \ -storepass changeit123 \ -keypass changeit123 \ -dname "CN=Demo, OU=Dev, O=Demo, L=City, ST=State, C=CN"

-keystore是输出文件名,-alias是别名,后面ant.properties里要一致。-keyalg RSA -keysize 2048是常见组合,-validity 36500大约100年,避免过期后无法覆盖安装。-storepass和-keypass在JDK 8下可以不同,但很多ANT脚本默认两者相同,建议先设成一样,减少变量。-dname里的字段可以按需改,不要包含真实个人信息,用演示值即可。

生成后验证别名:

keytool -list -v -keystore release.keystore -storepass changeit123

输出里能看到Alias name和Valid from,确认别名和有效期。然后把签名参数写进ant.properties:

# ant.properties:release签名参数 key.store=release.keystore key.store.password=changeit123 key.alias=demoalias key.alias.password=changeit123

key.store可以是相对路径,相对的是proj.android目录;如果keystore放在工程外,用绝对路径更稳。密码里如果包含#、=、:,在properties文件里要转义或换密码,不然会被截断。这个文件包含密码,不要提交到公开仓库,CI里用变量注入。

签名配置好后跑ant clean release,产物应该是已签名apk。验证签名用:

jarsigner -verify -verbose -certs bin/*-release.apk

输出里出现jar verified才算签名有效。如果报no manifest或certificate chain,先确认ant.properties里的别名和密码,再确认build.xml的release target是否真的读了这些属性。有的工程把签名target写在自定义-post-build里,属性名可能不是key.store,要看build.xml里的sign任务。

4.2 AndroidManifest.xml里的包名、版本号与权限

AndroidManifest.xml是ANT打包时合并资源、生成R文件的依据。最常改的是package、versionCode、versionName、uses-sdk和权限。下面是一个片段示例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.game" android:versionCode="101" android:versionName="1.0.1"> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:label="DemoGame" android:icon="@drawable/icon"> <meta-data android:name="CHANNEL" android:value="default" /> </application> </manifest>

package="com.demo.game"决定Java包名,也决定R文件路径。改包名时,src/下要有com/demo/game/目录,里面Java文件的package声明也要同步改,然后ant clean。只改Manifest不改src,编译会报package R does not exist或cannot find symbol。versionCode是整数,升级必须递增;versionName是显示给用户看的字符串。uses-sdk的minSdkVersion不要低于Cocos引擎和第三方so的要求,targetSdkVersion要与project.properties的target协调,但不必完全相同。

权限按实际功能加,不要从旧工程整段复制。ANT打包不会帮你剔除未使用权限,多出来的权限会在应用市场审核时被问。meta-data里的CHANNEL是渠道标记,值在打包脚本里替换。application的label和icon最好用资源引用,不要写死中文,避免编码问题。

改完Manifest后一定要ant clean,因为R.java在gen/里,Manifest里的包名或资源引用变了,旧R.java不会自动失效。只跑ant debug有时会用到缓存,出现“明明改了却还是旧包名”的玄学现象,clean一次就正常。

4.3 多渠道打包的ANT循环写法

多渠道最稳的做法不是改包名,而是把渠道号写进meta-data,包名保持一致。这样R文件和src都不用动,风险最低。下面用Python脚本替换Manifest里的渠道值,再循环调用ANT:

import re import subprocess from pathlib import Path manifest = Path("AndroidManifest.xml") original = manifest.read_text(encoding="utf-8") channels = ["default", "channel_a", "channel_b"] for ch in channels: # 只替换 CHANNEL 这个 meta-data 的 value pattern = r'(<meta-data\s+android:name="CHANNEL"\s+android:value=")[^"]*(")' replaced = re.sub(pattern, r'\g<1>' + ch + r'\g<2>', original) manifest.write_text(replaced, encoding="utf-8") # 每次 clean,避免资源缓存串渠道 subprocess.run(["ant", "clean", "release"], check=True) # 复制产物,按渠道命名 for apk in Path("bin").glob("*.apk"): target = Path("../out") / f"demo-{ch}.apk" target.parent.mkdir(parents=True, exist_ok=True) target.write_bytes(apk.read_bytes()) break # 还原Manifest,避免污染工作区 manifest.write_text(original, encoding="utf-8")

这段脚本的逻辑是:读原始Manifest,循环渠道列表,用正则只替换CHANNEL的android:value,然后ant clean release,把bin/下第一个apk复制到../out/并按渠道命名,最后还原Manifest。check=True让ANT失败时直接抛异常,不会继续复制旧包。break只取第一个apk,前提是bin/里只有一个release产物;如果工程同时产出多个apk,要按文件名过滤。

如果业务确实要求不同包名,比如马甲包,那就不是只改Manifest了:要改package、移动src/下的包目录、改所有Java文件的package声明、清gen/,再打包。这个流程容易漏改,建议用脚本做全量替换,并在打包后跑aapt dump badging核对包名。多包名场景下不要复用同一个R文件缓存,每次都要ant clean。

渠道循环里还有一个细节:ant.properties里的签名配置对所有渠道共用,如果不同渠道用不同签名,要在循环里切换ant.properties或传-Dkey.store=...。传参前确认build.xml里的签名任务读的是哪个属性名,不要凭猜测传。

5. 避坑排查:ANT安卓打包最常见的五类翻车

5.1 现象:aapt not found

现象很直接:ant clean debug跑到资源编译阶段,日志里出现aapt: not found或Execute failed: java.io.IOException: Cannot run program "aapt"。有时前面还会打印build-tools路径,但那个路径下没有aapt可执行文件。

原因通常有两个:一是project.properties里的sdk.buildtools写了一个SDK里不存在的版本,ANT按这个版本拼路径,拼出来的目录是空的;二是只装了最新build-tools,旧ant脚本按固定目录名找,找不到。Windows下还有第三种:aapt.exe存在,但路径里有空格,ANT没有加引号,执行时被截断。

解决时先列目录核对:

ls $ANDROID_SDK_ROOT/build-tools grep 'sdk.buildtools' project.properties

把project.properties里的版本改成ls结果里真实存在的目录名,再跑ant clean debug。如果目录名对但仍然找不到,检查build-tools/版本/下有没有aapt或aapt.exe;有的SDK安装器只装了一半。Windows路径有空格时,把SDK移到无空格目录,比如D:/android-sdk,不要放在C:/Program Files/...下面。

5.2 现象:Source option 6 is no longer supported

现象是Java编译阶段失败,日志里写Source option 6 is no longer supported. Use 8 or later,后面跟着javac任务和build.xml行号。工程代码可能一行没改,换了一台机器就报。

原因是这台机器上的JDK不是8,可能是11或17。旧SDK的ant脚本默认source=1.6,新JDK不再支持。ANT只是调用者,改ANT版本没用。

解决分两步:先确认java -version和javac -version都是8,并且JAVA_HOME指向JDK 8;如果机器上必须保留高版本JDK,就给这个工程单独配一个终端环境,把JAVA_HOME临时指到JDK 8。改build.xml里的source和target到1.8也能过编译,但会影响所有引用这个build.xml的工程,不如切JDK干净。切完JDK后记得ant clean,旧gen/里的class可能是高版本JDK编的。

5.3 现象:ndk-build 报 APP_PLATFORM 或 toolchain 错误

现象是-pre-build阶段调用ndk-build失败,日志里出现Android NDK: APP_PLATFORM android-XX is not supported或toolchains/.../prebuilt路径不存在。Java层还没开始编译就停了。

原因是NDK版本和工程里的jni/Application.mk不匹配。旧工程写APP_PLATFORM := android-9,新NDK可能最低只支持android-16或更高;旧工程写APP_STL := gnustl_static,新NDK已经移除gnustl。反过来,NDK太旧也会不认新ABI。

解决时先看jni/Application.mk:

cat jni/Application.mk

把APP_PLATFORM改到NDK支持的最低版本,把APP_STL改成c++_static或c++_shared(按第三方so的依赖选),再跑ndk-build clean和ant clean debug。如果工程依赖的第三方so是用gnustl编的,不要单独换STL,要么找对应版本NDK,要么让第三方库重新出包。改完Application.mk后,libs/下的旧so要先删,避免新旧ABI混在一起。

5.4 现象:签名后安装解析失败

现象是ant release显示成功,bin/下有apk,但安装时报“解析包时出现问题”或“应用未安装”。用jarsigner -verify可能显示已签名,但安装就是不过。

原因常见三种:ant.properties里的key.alias和keystore里的别名不一致;key.store.password和key.alias.password写反;AndroidManifest.xml里的package和R文件包名不一致,导致apk内部结构损坏。还有一种少见情况:versionCode不是整数,或者uses-sdk的minSdkVersion高于设备系统版本。

排查顺序:先keytool -list确认别名,再jarsigner -verify确认签名,最后aapt dump badging看package和versionCode。如果签名有效但安装失败,重点看aapt dump badging输出的包名是否和预期一致。包名不对时,回到Manifest和src/目录同步修改,ant clean后重打。密码写反时,日志不一定报错,因为签名任务可能用了空密码或默认值,产物看起来有签名但实际上不完整。

提示:ant.properties里的密码不要用#开头,properties文件会把#当注释。密码里带等号时,用\=转义,或者直接换一个不含特殊字符的密码。

5.5 现象:路径带空格或中文导致构建中断

现象是构建到一半报Cannot run program、The system cannot find the path specified,或者aapt报某个资源文件不存在,但文件明明在。日志里的路径被截成两段,或者中文变成乱码。

原因是ANT和旧SDK脚本对路径分隔符、空格、编码处理不完整。工程放在D:/工作/demo game/这种路径下,空格会把demo game拆开,中文在Windows控制台编码下可能变成问号。local.properties里的反斜杠也会被当转义。

解决时把工程和SDK都移到无空格、无中文的路径,比如D:/work/demo和D:/work/android-sdk。local.properties里的路径统一用正斜杠。Windows控制台如果还有中文乱码,在跑ANT前执行chcp 65001切到UTF-8,或者把ANT_OPTS加上-Dfile.encoding=UTF-8。改完路径后删掉bin/和gen/,重新ant clean debug,不要用旧产物覆盖。

6. 验证与提速:从apk反查配置到增量打包的固定手法

6.1 用aapt dump badging验证包

出包后不要只看文件名,先用aapt dump badging反查apk内部信息:

AAPT=$ANDROID_SDK_ROOT/build-tools/28.0.3/aapt $AAPT dump badging bin/demo-release.apk | head -n 20

输出里重点看package: name='com.demo.game' versionCode='101' versionName='1.0.1'、launchable-activity、native-code: 'armeabi-v7a' 'arm64-v8a'。包名和版本号对不上,说明Manifest被改错或用了旧包;native-code缺架构,说明jni/Application.mk或libs/里的so不全。launchable-activity不对,安装后桌面可能没有图标。

6.2 ANT增量与clean的取舍

ANT的debugtarget会按时间戳跳过未变文件,日常改Java代码可以增量;但改AndroidManifest.xml、project.properties、res/资源名、签名配置后,必须ant clean。我习惯是:本地联调ant debug,出渠道包和release包一律ant clean release。clean多花几十秒,能避免“改了没生效”的假象。下面这张表是取舍依据:

场景命令原因
只改Java逻辑ant debug增量编译快
改Manifest包名/权限ant clean debugR.java和合并资源要重建
改签名配置ant clean release避免旧签名产物混入
出渠道包ant clean release防止渠道资源串包

6.3 一个固定检查清单

每次出包前按这张表过一遍,比出事后再翻日志省时间:

检查项命令或位置通过标准
JDK版本java -version1.8.x
ANT可用ant -version输出版本号
SDK路径local.propertiessdk.dir目录存在
编译目标project.propertiestarget与platforms目录一致
签名参数ant.properties别名、密码与keystore一致
包名版本aapt dump badgingpackage/versionCode符合预期
so架构`unzip -l apkgrep lib`

血泪经验是:ANT打包的坑大多不在代码,而在“以为改过了”。从那以后我每次出release包都强制走一遍ant clean release,再用aapt dump badging核对包名、版本号和so架构,最后才把apk放进发布目录。这套配置模板和检查表我放在工程根目录的tools/ant-android/下,拿到后先对照第2章的目录树核一遍路径,再改local.properties和ant.properties两个文件,基本能少走一半弯路。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表