Embarcadero 11中Android AlarmManager定时任务及线程阻塞问题求助
一、AlarmManager定时没触发?排查与修复方案
适配高版本Android的API使用
Android 12+的Doze模式会限制普通闹钟触发,别用setRepeating,得改用setExactAndAllowWhileIdle(单次触发),重复任务需要每次触发后重新调度。测试15分钟间隔的代码参考:procedure TAndroidService.ScheduleTwiceDailyEvent; var AlarmMgr: JAlarmManager; Intent: JIntent; PendingIntent: JPendingIntent; FirstTriggerTime: Int64; begin AlarmMgr := TJAlarmManager.Wrap(TJAlarmManager.JavaClass.getInstance(getApplicationContext)); // 关联自定义Receiver Intent := TJIntent.JavaClass.init(getApplicationContext, TJMyReceiver.JavaClass); // 安卓12+必须加FLAG_IMMUTABLE PendingIntent := TJPendingIntent.JavaClass.getBroadcast(getApplicationContext, 0, Intent, TJPendingIntent.JavaClass.FLAG_UPDATE_CURRENT or TJPendingIntent.JavaClass.FLAG_IMMUTABLE); // 设置首次触发为当前时间+15分钟 FirstTriggerTime := TJSystem.JavaClass.currentTimeMillis + 15 * 60 * 1000; // 用RTC_WAKEUP确保唤醒设备 AlarmMgr.setExactAndAllowWhileIdle(TJAlarmManager.JavaClass.RTC_WAKEUP, FirstTriggerTime, PendingIntent); end;另外Android 13+需要添加
POST_NOTIFICATIONS权限,在Manifest中加入<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />,并在运行时主动请求权限。验证AlarmManager是否注册成功
- 用ADB命令查看已注册闹钟:
adb shell dumpsys alarm,搜索你的应用包名,就能看到已设置的闹钟明细(触发时间、类型等)。 - 在Receiver的
onReceive方法中添加日志输出,比如TAndroidService.Log('闹钟已触发!'),再通过adb logcat -s 你的应用标识查看日志,确认是否触发。
- 用ADB命令查看已注册闹钟:
二、自定义Receiver在APK Manifest中缺失?修复步骤
不要手动修改Debug目录的Manifest
Delphi编译时会重新生成该文件,手动修改无效。正确做法是:在项目的Deployment选项卡中添加自定义的AndroidManifest.template.xml,在模板中写入Receiver配置:<receiver android:name="com.你的公司名.你的应用名.MyReceiver" android:exported="false"> <intent-filter> <action android:name="com.你的公司名.你的应用名.ALARM_TRIGGER" /> </intent-filter> </receiver>确保Pascal代码中的Receiver类名与配置一致,比如用
[RegisterReceiver(TJMyReceiver)]注解绑定。配置Delphi使用自定义模板生成Manifest
打开项目选项→Application→Manifest,勾选Custom Android Manifest,指定你的模板文件路径。编译后查看Android64\Debug\AndroidManifest.xml,确认Receiver条目存在后再安装APK。
三、Service中开线程仍阻塞GUI?正确写法
Service默认运行在主线程,直接在Service主线程执行Sleep会阻塞GUI,必须把定时逻辑放到子线程中,UI操作需同步回主线程:
procedure TAndroidService.ServiceCreate(Sender: TObject); begin TThread.CreateAnonymousThread( procedure begin while not Terminated do begin // 定时检查逻辑写在这里 TThread.Sleep(15 * 60 * 1000); // 如需更新UI,必须通过Synchronize切回主线程 TThread.Synchronize(nil, procedure begin // 比如更新控件、发送通知等GUI操作 end); end; end).Start; end;
注意:子线程中绝对不能直接操作GUI控件,必须用Synchronize或Queue同步到主线程。
四、Embarcadero Android开发进阶学习建议
- 优先研读官方文档的Android Native Integration章节,重点掌握JNI调用、Manifest配置、后台任务管理三大核心内容。
- 观看官方YouTube频道的Delphi Android Deep Dive系列视频,都是实战导向的内容,覆盖Service、AlarmManager、权限处理等高频痛点。
- 参与RAD Studio论坛的Android专区讨论,学习开发者们遇到的实际问题及解决方案,比纯看书更高效。
- 练习自定义JNI绑定,手动实现Delphi与Android原生API的交互,比如封装AlarmManager的高级功能,熟练后能解决大部分原生适配问题。
内容的提问来源于stack exchange,提问作者Lanmi

