You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Embarcadero 11中Android AlarmManager定时任务及线程阻塞问题求助

问题解决与实战建议

一、AlarmManager定时没触发?排查与修复方案

  1. 适配高版本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" />,并在运行时主动请求权限。

  2. 验证AlarmManager是否注册成功

    • 用ADB命令查看已注册闹钟:adb shell dumpsys alarm,搜索你的应用包名,就能看到已设置的闹钟明细(触发时间、类型等)。
    • 在Receiver的onReceive方法中添加日志输出,比如TAndroidService.Log('闹钟已触发!'),再通过adb logcat -s 你的应用标识查看日志,确认是否触发。

二、自定义Receiver在APK Manifest中缺失?修复步骤

  1. 不要手动修改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)]注解绑定。

  2. 配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 14:29:52