一、问题现象
FireMonkey应用用命令行 MSBuild Deploy 部署到 Android 虚拟机(模拟器 emulator-5554,x86_64,Android 9 / API 28)以后,画面一直停在 Delphi 默认的启动图上,进不了主界面。与此同时 RAD Studio 报:
Unable to start LLDB kernel: 'd:\program files (x86)\embarcadero\studio\37.0\bin\windows\lldb\lldb-mi.exe'.同一个程序在 Windows 平台一切正常,只在 Android 出现。
二、先别急着怀疑 IDE:把问题分成三类
“停在启动界面”在 Android 上至少有三完全不同的成因,处置方式也不一样:
| 类型 | 特征 | 常见原因 |
|---|---|---|
| 崩溃型 | 进程起不来或秒退,反复重启 | 主线程未捕获异常、native so 加载失败、ABI 不匹配 |
| 卡死型 | 进程存活但 CPU 100% 或长时间不动 | 启动路径做了同步阻塞 IO(网络/数据库/串口)、死锁 |
| 遮挡型 | 进程存活,界面其实已画好但看不到 | 系统 Splash 窗口没被移除、窗口层级/全屏设置问题 |
这里有个容易混淆的点:Android 的启动画面有两层。
- 系统级 Splash 窗口:由 WindowManager 创建,窗口名是
Splash Screen <包名>,在应用提交第一帧之前一直盖在最上面。 - 应用级启动图:Delphi 在
AndroidManifest/styles.xml里配的splash_image_def.xml,由 FMX 自己绘制。
所以“看到启动图”并不等于“应用还活着”,它很可能只是系统那张占位窗口。
三、排查过程(命令与判读)
1. 进程是否存活
adb -s emulator-5554 shell ps | grep MyApp
adb -s emulator-5554 shell top -d 1 | grep MyApp本次现象:64 位包(arm64-v8a)进程一起来就 has died: fore TOP,反复重启;32 位包(armeabi-v7a)进程活着但 CPU 一直 100%。
2. 窗口层级——是谁盖住了界面
adb -s emulator-5554 shell dumpsys window | grep -iE "mCurrentFocus|mObscuringWindow|Splash Screen"关键证据:
mObscuringWindow=Window{... Splash Screen com.youcom.MyApp}说明 Activity 已经创建,但 FMX 始终没有提交第一帧,系统占位窗口不会被撤掉。
3. 抓一次干净的 logcat
adb -s emulator-5554 logcat -c
adb -s emulator-5554 shell am force-stop com.youcom.MyApp本次抓到:
W/System.err(13814): java.io.IOException: Cleartext HTTP traffic to 147.*.208.123 not permitted4. 有没有 native crash
adb -s emulator-5554 shell ls -lt /data/tombstones/
adb -s emulator-5554 logcat -d | grep -iE "DEBUG|libc|CrashArm64|native_bridge"没有 tombstone,说明不是 native 段错误,而是框架层异常把启动流程打断了。
四、根因
是两个问题叠加在一起:
- Android 9(API 28)起,
targetSdkVersion >= 28的应用默认禁止明文 HTTP。 工程的 DEBUG 版接口地址是http://147.*.208.123:82/...,请求在连接阶段就被拦掉。 - 更要命的是启动路径没有异常保护。 这个请求是在
TFrmMain.FormCreate → ShowFrame(FrmStart) → TFrmStart.AfterConstruction里同步调用的,而PosStartupCertify()没有try/except。HTTP 层抛出的异常一路冒泡到Application.CreateForm(TFrmMain, FrmMain),主窗体创建失败,FMX 永远画不出第一帧,系统 Splash 不消失,看起来就是“卡在启动界面”;进程异常退出后又被 AMS 拉起,于是反复重启。
IDE 那条 Unable to start LLDB kernel 是结果而不是原因:目标进程秒退,lldb-mi 附加不上就报这个错误。
五、解决方案
1. 设置允许明文 HTTP(开发/内网环境)
改 AndroidManifest.template.xml,在 <application> 上加一行:
<application android:persistent="%persistent%"
android:label="%label%"
android:hardwareAccelerated="%hardwareAccelerated%"
android:usesCleartextTraffic="true"
android:resizeableActivity="false">踩坑提醒:命令行 MSBuild Deploy 会复用已经生成的 AndroidManifest.xml / AndroidManifest-merged.xml,改完模板如果没触发重新生成,要先手工删掉这两个中间产物再 Deploy,否则包里还是旧清单。
不想全站放开的话,更规范的做法是只放行指定域名:
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">147.*.208.123:82</domain>
</domain-config>
</network-security-config>正式环境走 HTTPS 就完全不需要放开。
2. 启动接口做异常兜底,联网失败降级而不是崩溃
function PosStartupCertify(): TWebResponse;
begin
Result.success := False; // 先给默认值
Result.msg := '';
try
WebParam := SO();
WebParam.S['command_id'] := 'APPSTART';
...
Response := TRequest.New.BaseURL(WEB_URL)
.UserAgent(UserAgent)
.Accept('application/json')
.AddBody(WebParam.AsJSON)
.Post;
{ 解析返回值 ... }
except
on E: Exception do
begin
Result.id := -1;
Result.msg := '网络初始化异常,请检查网络配置';
WriteError('NetOperation', E.Message, '启动\联网');
end;
end;
end;调用侧保持原来的失败分支(DisableTrade(ResponseContent.msg)),这样即使服务端不通,用户也能进主界面并看到可读提示,而不是白屏等死。
顺带一个 Delphi 常见坑:Result 是记录型局部变量,只有字符串、接口这类托管字段会被自动初始化,Boolean/Integer 是随机值。原代码在“成功但没有 data 节点”的分支里没给 success 赋值,就是这个隐患。
3. 原则:FormCreate / AfterConstruction 里不要做可能抛异常或耗时的外部 IO
要么异步(TTask + 回调,失败只提示),要么等窗体显示出来再拉数据。启动阶段任何冒泡出 Application.CreateForm 的异常,表现都是“卡在启动界面”。
六、验证
export BDS="D:\\Program Files (x86)\\Embarcadero\\Studio\\37.0"
/c/Windows/Microsoft.NET/Framework64/v4.0.30319/MSBuild.exe MyApp.dproj \
//t:Deploy //p:Platform=Android64 //p:Config=Debug //v:normal //nologo- Android64 / Android / Win32 三个平台编译 0 error;
- 装机后 logcat 出现
Displayed com.youcom.MyApp/...FMXNativeActivity: +1s667ms,表示第一帧已经提交; mObscuringWindow消失,Cleartext 报错 0 条,进程 pid 稳定存活,截图就是主界面。
另外记录一个命令行构建差异://t:Build 会给 dcc32 加 -B(强制重编全部单元),这时第三方控件(OrangeUI)里的 {$I FrameWork.inc} 会因为搜索路径没有该 inc 所在目录而报 F1026: File not found;IDE 里增量编译不会暴露这个问题。把对应目录补进 DCC_UnitSearchPath 即可。
七、这类问题的通用分析套路
- 先分类:崩溃型 / 卡死型 / 遮挡型。用
ps+top+dumpsys window三条命令,五分钟内就能分完。 - 再分层:Java 层异常看
System.err、AndroidRuntime;native 看 tombstone 和DEBUG进程;渲染问题看dumpsys SurfaceFlinger。 - 一定要清缓冲后冷启动再抓日志(
logcat -c),滚动日志里的历史噪声会误导判断。 - 策略类问题优先怀疑平台差异:Android 9 明文 HTTP、Android 11 包可见性(
<queries>)、Android 13 通知权限、以及提升targetSdkVersion带来的一揽子行为变化。 - 模拟器不等于真机:x86_64 + houdini 转译 ARM 包,存在转译失败和无法 ptrace 调试的情况。报 LLDB 起不来时,先确认 ABI 与调试器兼容性,再换真机复现,不要一头扎进 IDE 配置里。
- Delphi / FMX 特有的几个坑:启动期异常冒泡、
Result未初始化、GBK 源码在命令行工具链下的编码处理、控件应当在.fmx里静态声明而不是代码动态创建。
注:本篇文章由AI辅助生成,其实问题也是AI解决的....

还不快抢沙发