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,导致服务无法正常启动或被系统快速杀死,中断重复触发流程。
解决方法:
- 在
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;
- 在
AndroidManifest.xml中添加前台服务权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
核心问题3:三星设备电池优化限制
Galaxy A12(Android 11)的三星系统有严格的电池优化策略,默认会限制后台闹钟和服务的运行。需要引导用户将应用加入电池优化白名单:
- 在代码中申请忽略电池优化的权限:
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;
- 在
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
相关产品推荐
相关产品推荐

