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

Android AlarmManager启动的Service仅执行一次问题排查

AlarmManager重复闹钟仅执行一次的问题分析与解决(Android 11 + Delphi)

核心问题1:setRepeating参数传递错误

你的setRepeating调用中,第三个参数误传为绝对时间(当前时间+120秒的毫秒值),但该参数要求的是两次闹钟触发的间隔毫秒数。这会导致系统计算的下一次触发时间异常(比如第一次触发后,下一次触发时间被设为当前时间+一个巨大的数值),从而只执行一次。

修正后的SetupAlarm代码:

procedure TMainForm.SetupAlarm();
var
  Intent: JIntent;
  PendingIntent: JPendingIntent;
  FirstTriggerTime: Int64;
  IntervalMillis: Int64;
begin
  try
    Intent := TJIntent.Create;
    Intent.setClassName(TAndroidHelper.Context, StringToJString('com.embarcadero.services.RemoteService'));

    PendingIntent := TJPendingIntent.JavaClass.getService(TAndroidHelper.Context, 0, Intent, TJPendingIntent.JavaClass.FLAG_UPDATE_CURRENT);

    FirstTriggerTime := TJSystem.JavaClass.currentTimeMillis(); // 立即触发首次任务
    // FirstTriggerTime := getTimeAfterInSecs(120); // 可选:延迟2分钟执行首次任务
    IntervalMillis := 120 * 1000; // 固定间隔2分钟(单位:毫秒)

    TAndroidHelper.AlarmManager.setRepeating(TJAlarmManager.JavaClass.RTC_WAKEUP, FirstTriggerTime, IntervalMillis, PendingIntent);
  except
    on E: Exception do
      Log(E.Message);
  end;
end;

function TMainForm.getTimeAfterInSecs(Seconds: Integer): Int64;
var
  Calendar: JCalendar;
begin
  Calendar := TJCalendar.JavaClass.getInstance;
  Calendar.add(TJCalendar.JavaClass.SECOND, Seconds);
  Result := Calendar.getTimeInMillis;
end;

核心问题2:Android 8+后台服务启动限制

Android 8.0(API 26)之后,系统禁止后台直接启动普通服务,必须使用前台服务,否则会抛出IllegalStateException,导致服务无法正常启动或被系统快速杀死,中断重复触发流程。

解决方法:

  1. 在RemoteService的DoStartCommand中立即启动前台服务,显示低优先级通知(Android 8+必需):
function TRemoteServiceDM.DoStartCommand(const AStartIntent: JIntent; AFlags, AStartID: Integer): Integer;
var
  NotificationBuilder: JNotificationCompat_Builder;
  Notification: JNotification;
  ChannelID: JString;
begin
  try
    // 创建通知渠道(Android 8+强制要求)
    ChannelID := StringToJString('MyServiceChannel');
    if TJBuild_VERSION.JavaClass.SDK_INT >= TJBuild_VERSION_CODES.JavaClass.O then
    begin
      var Channel := TJNotificationChannel.JavaClass.init(ChannelID, StringToJString('后台服务通知'), TJNotificationManager.JavaClass.IMPORTANCE_LOW);
      var NotificationManager := TJNotificationManager.Wrap(TAndroidHelper.Context.getSystemService(TJContext.JavaClass.NOTIFICATION_SERVICE));
      NotificationManager.createNotificationChannel(Channel);
    end;

    // 构建前台通知
    NotificationBuilder := TJNotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context, ChannelID);
    NotificationBuilder.setContentTitle(StringToJString('服务运行中'));
    NotificationBuilder.setContentText(StringToJString('正在执行SOAP操作'));
    NotificationBuilder.setSmallIcon(TAndroidHelper.Context.getApplicationInfo().icon);
    Notification := NotificationBuilder.build();

    // 启动前台服务
    startForeground(1, Notification);

    AcquireWakeLock;
    try
      // 执行SOAP操作
      GetSOAPService().HelloWorld;
    except
      on E: Exception do
        Log(E.Message);
    end;

    ReleaseWakeLock;
    // 完成任务后可停止前台服务(隐藏通知)
    stopForeground(True);
  except
    on E: Exception do
      Log(E.Message);
  end;
  Result := TJService.JavaClass.START_STICKY;
end;
  1. 在AndroidManifest.xml中添加前台服务权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

核心问题3:三星设备电池优化限制

Galaxy A12(Android 11)的三星系统有严格的电池优化策略,默认会限制后台闹钟和服务的运行。需要引导用户将应用加入电池优化白名单:

  1. 在代码中申请忽略电池优化的权限:
procedure TMainForm.RequestIgnoreBatteryOptimizations;
var
  Intent: JIntent;
begin
  if TJBuild_VERSION.JavaClass.SDK_INT >= TJBuild_VERSION_CODES.JavaClass.M then
  begin
    var PowerManager := TJPowerManager.Wrap(TAndroidHelper.Context.getSystemService(TJContext.JavaClass.POWER_SERVICE));
    if not PowerManager.isIgnoringBatteryOptimizations(TAndroidHelper.Context.getPackageName()) then
    begin
      Intent := TJIntent.JavaClass.init(TJSettings.JavaClass.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
      Intent.setData(TJUri.JavaClass.parse(StringToJString('package:' + TAndroidHelper.Context.getPackageName())));
      TAndroidHelper.Activity.startActivity(Intent);
    end;
  end;
end;
  1. 在AndroidManifest.xml中添加对应权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

其他注意事项

  • 确保RemoteService在AndroidManifest.xml中正确注册,根据需求设置android:exported属性:
<service android:name="com.embarcadero.services.RemoteService" android:exported="false" />
  • 完成SOAP操作后立即释放PARTIAL_WAKE_LOCK,减少不必要的电量消耗。
  • Android API 19之后,setRepeating的间隔会被系统自动调整以节省电量,若需要精确触发,建议使用setExactAndAllowWhileIdle(Android 6+),并在每次任务执行完成后重新设置下一次闹钟,替代重复闹钟机制。

内容的提问来源于stack exchange,提问作者Jaroslav Dibitanzl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:34:55