如何在FireMonkey移动应用运行时捕获可读异常堆栈跟踪?
FireMonkey移动应用(Android/iOS)捕获可读异常堆栈的方案
针对Delphi 11.3(Alexandria)和12.1(Athens)的FireMonkey移动应用,以下是几种捕获可读异常堆栈的实用方案,替代Windows平台的jclDebug、madEcept等工具:
一、用Delphi内置能力直接获取
从Delphi 10.3开始,FireMonkey移动平台已经支持直接读取异常的堆栈信息,关键是要正确设置编译选项:
- 开启调试信息:在项目选项的「Compiler」→「Linking」里,勾选「Include debug information」;Android额外开启「Generate stack frames」,iOS在「iOS Build」中开启「Debug information」。
- 直接调用
Exception.StackTrace属性:捕获异常时,直接读取这个属性就能拿到Delphi层面的堆栈。注意Release版本会因编译器优化丢失部分信息,建议要么开调试信息的Release包,要么用Debug版本排查问题。 - 线程堆栈补充:如果需要获取当前线程的堆栈(非异常场景),可以用
TThread.CurrentThread.StackTrace。
二、第三方成熟工具(开箱即用)
这些工具和Windows平台的体验一致,配置简单:
- EurekaLog:支持Android/iOS,安装对应Delphi版本的插件后,在项目选项里启用它,就能自动捕获全局异常,生成带详细堆栈、线程信息、系统环境的报告,还能把报告存本地或传服务器。
- madExcept:同样支持移动平台,配置后会自动生成符号解析后的堆栈,还能自定义报告格式和输出方式,适合需要深度定制的场景。
三、手动实现跨平台堆栈捕获(无依赖)
如果不想用第三方工具,可以自己写代码获取原生平台的堆栈:
Android平台
利用JNI获取Java层的堆栈:
uses Androidapi.JNI.JavaTypes, Androidapi.Helpers; function GetAndroidNativeStackTrace(E: Exception): string; var JEx: JThrowable; StackElements: TJavaObjectArray<JStackTraceElement>; I: Integer; begin Result := ''; if E is EJNIException then begin JEx := EJNIException(E).JNIException; StackElements := JEx.getStackTrace; for I := 0 to StackElements.Length - 1 do Result := Result + StackElements.Items[I].toString + sLineBreak; end; end;
iOS平台
通过ObjC的backtrace函数获取原生堆栈:
uses Objcapi, Macapi.ObjectiveC, Macapi.Foundation; function GetiOSNativeStackTrace: string; var FrameCount: Integer; CallStack: Pointer; StackSymbols: PPAnsiChar; I: Integer; begin Result := ''; FrameCount := backtrace(nil, 0); CallStack := malloc(FrameCount * SizeOf(Pointer)); try FrameCount := backtrace(CallStack, FrameCount); StackSymbols := backtrace_symbols(CallStack, FrameCount); if Assigned(StackSymbols) then begin for I := 0 to FrameCount - 1 do Result := Result + StackSymbols[I] + sLineBreak; free(StackSymbols); end; finally free(CallStack); end; end;
符号解析(Release版本必备)
Release版本的堆栈是内存地址,需要用Delphi自带工具解析:
- iOS:用
dsymutil工具结合编译生成的.dSYM文件,把地址转换成代码行。 - Android:用
addr2line工具(Android NDK自带),配合.map文件解析地址。
四、全局异常捕获配置
不管用哪种方案,建议注册全局异常处理,避免应用直接崩溃并保存日志:
procedure GlobalExceptionHandler(Sender: TObject; E: Exception); var FullStackTrace: string; begin // 先获取Delphi层面的堆栈 FullStackTrace := E.StackTrace; // 补充原生平台堆栈 {$IFDEF ANDROID} FullStackTrace := FullStackTrace + sLineBreak + 'Android Native Stack:' + sLineBreak + GetAndroidNativeStackTrace(E); {$ENDIF} {$IFDEF IOS} FullStackTrace := FullStackTrace + sLineBreak + 'iOS Native Stack:' + sLineBreak + GetiOSNativeStackTrace; {$ENDIF} // 保存到本地文件或上传服务器 // 示例:SaveLogToFile是自定义的日志保存函数 SaveLogToFile('CrashLog_' + FormatDateTime('yyyyMMdd_hhmmss', Now) + '.txt', Format('Exception: %s'#13#10'Message: %s'#13#10'Stack Trace:'#13#10'%s', [E.ClassName, E.Message, FullStackTrace])); end; initialization Application.OnException := GlobalExceptionHandler;
内容的提问来源于stack exchange,提问作者AlexLL
相关产品推荐
相关产品推荐

