话说直接修改程序项目的 AndroidManifest.template.xml,将 API-level 从通配符改为写死的 30 后,可以编译发布出 AAB 文件,而且这个 AAB 文件上传到 Google play 它没提示 API-level 是 29 不合格,算是通过了。但是,如果选择程序项目的 Configuration 为 Development,手机插上电脑,重新编译发布安装到手机上,安装失败,错误提示:INSTALL_PARSE_FAILED_NO_CERTIFICATES。
俺满世界搜索,用尽了各种搜索工具,搜了一堆东西出来,有类似的问题,但都是 Xamarin 的或者其它什么鬼开发工具的,没有 Delphi 的。找不到答案。
然后去了一个德国的 Delphi 论坛发了个帖子提问,然后有个大神给出了解决方案。一试,果然有效。这是那个德国网站我发的帖子的地址:
https://en.delphipraxis.net/topic/5883-delphi-1042-ce-support-android-api-30/
这里总结一下具体的原因和做法:
It's because Delphi 10.4.2 is using Jarsigner instead of APKSigner
For sign apk by Jarsigner ( APK Signature Scheme v2 ) you need:
1. Edit CodeGear.Deployment.Targets add section:
2. add line _AndroidApkSigner; in section
...
_CheckKeyStore;
_AndroidSign;
_AndroidZipAlign;
_AndroidApkSigner;
_CheckAPKFileOutputResult;
_CheckAABFileOutputResult;
_DeleteAndroidAssetsInfoFile;
...
备注:CodeGear.Deployment.Targets 这个文件,在 Delphi 的安装目录底下。
3. Create New IDE Environment Variable ApkSigner with value (SDK path)\android-sdk-windows\build-tools\29.0.2\apksigner.bat
比较修改前和修改后的 CodeGear.Deployment.Targets 文件
简单说来,对于 Delphi 10.4.2 编译发布的 Android 程序,要支持到 API-level 30,需要做:
1. 修改 AndroidManifest.template.xml,将原本的:
修改为:
然后 Build 以后,创建的 AndroidManifest.xml 文件的内容就从原本的:
自动变成了:
2. Delphi 原本安装时带来的 SDK 支持的平台只有29,路径如下:
C:\Users\Public\Documents\Embarcadero\Studio\21.0\CatalogRepository\AndroidSDK-2525-21.0.40680.4203\platforms\android-29
需要执行 SDKManager.bat 去下载升级30的,升级方法见上一篇文章,升级后在以下路径:
C:\Users\Public\Documents\Embarcadero\Studio\21.0\CatalogRepository\AndroidSDK-2525-21.0.40680.4203\platforms
底下有两个文件夹,一个是原来就有的 android-29,一个是新增的 android-30
然后修改 DELPHI IDE 菜单里面关于安卓的 SDK 的 API-level 的指向的配置。做法见上一篇文章。
3. 最后就是修改签名方式,如本文前面所述。
按照上述方式,新建一个 FireMonkey 工程,编译目标选择安卓64,没有任何问题。不管是 Development 模式下直接安装到手机里面,还是 Application store 模式下编译发布为 AAB 文件,一切顺利。
但对于我的一个老的工程,在 debug 模式下没有问题,在 Release 模式下,用 Development 方式编译安装到手机里面,INSTALL_PARSE_FAILED_NO_CERTIFICATES 这个错误依然存在,安装失败。拿它的 xml 文件和安装成功的工程的 xml 文件仔细来回对比,看不出哪里有不对。
于是,俺干脆重建一个新的工程,把老工程的所有代码都加进去,对于这个新工程,同样做如上描述的修改,编译发布安装到手机成功,并且 APP 运行也很正常。
我的老工程,有个函数里面,定义了一个局部变量,整数类型。该函数里面用到那个变量之前,没有做初始化。通常 Delphi 会自动把整数初始化为 0. 之前在安卓下,API level 为 29,程序运行没有任何问题。但只要改为 30,就有问题。追查好久才发现是该变量没有初始化导致。在函数一开始给该变量赋值 0,然后在 API level 30 下编译跑起来,问题消失。
经验教训总结:局部变量一定要自己用代码初始化,不要依赖 Delphi 编译器的默认。