Delphi XE10.2开发Android后台服务启动报错及iTask实现求助
问题排查与优化建议
一、初始服务启动问题(访问违规+服务无法启动)
1. 访问违规错误根源
你提到调用Fservice.StartService('TheService')触发访问违规,移除服务jar文件后错误消失但服务无法启动。这大概率是服务jar文件与Delphi XE10.2 Tokyo的Android SDK版本不兼容导致的:
- Delphi XE10.2对应Android SDK API 25(Android 7.1),如果你的服务jar是针对更高/更低版本编译的,会引发JNI层面的访问错误。
- 移除jar后,服务缺失了必要的原生依赖,自然无法正常启动。
修复建议:
- 确保服务jar基于Delphi XE10.2配套的Android SDK编译,或者直接使用Delphi内置的
TLocalService组件,避免手动引入第三方jar。 - 检查
AndroidManifest.template.xml中服务的注册是否正确,格式如下:<service android:name="com.embarcadero.services.TheService" /> - 改用更可靠的服务启动方式,避免
TLocalServiceConnection的潜在问题:if switchnotificationenable.IsChecked then begin var Intent := TJIntent.JavaClass.init(TAndroidHelper.Context, TJTheService.JavaClass); TAndroidHelper.Context.startService(Intent); end;
二、iTask循环实现的问题排查
你改用iTask+Sleep的方式检查时间并发送通知,这段代码存在几个关键问题:
1. 线程安全风险
TNotification相关操作(如ScheduleNotification)必须在UI线程执行,但你在后台线程的TTask.Run中直接调用,会引发线程安全问题甚至崩溃。- 旧版
TQuery组件不适合在后台线程直接操作,容易出现数据集异常或连接泄漏。
2. 循环逻辑的效率与准确性问题
Sleep(30000)会阻塞线程,无法及时响应Checking停止信号,建议改用TEvent实现可中断的等待。SameText(FormatDateTime('HH-MM',NotifTime),formatdatetime('HH-MM',today))的时间匹配方式存在临界点误差,比如系统时间刚好卡在30秒间隔边缘时会错过触发,建议直接比较小时和分钟数值。
3. 资源泄漏问题
TStringList创建后未调用Free,会导致内存泄漏。- 数据库查询循环中反复执行
SQL.Clear和Open,未正确关闭数据集,可能引发连接泄漏。
三、优化后的代码示例
针对上述问题,给出调整后的实现:
procedure TDM.AndroidServiceCreate(Sender: TObject); var TheINIFIle: string; config: TStringList; notiftime: TTime; CheckEvent: TEvent; begin TheINIFILE := TPath.Combine(TPath.GetSharedDocumentsPath, 'NotificationParams.ini'); if not FileExists(TheINIFILE) then Exit; config := TStringList.Create; try config.LoadFromFile(TheINIFILE); notiftime := StrToTime(config.Values['TimeNotification']); TimerConnection.Database := config.Values['DatabasePath']; if not TimerConnection.Connect then Exit; finally config.Free; // 及时释放资源 end; // 创建可中断事件替代Sleep,支持及时停止循环 CheckEvent := TEvent.Create(nil, False, False, ''); TTask.Run(procedure var Today: TTime; Query: TFDQuery; // 使用FireDAC的线程安全数据集 NeedNotify: Boolean; begin Query := TFDQuery.Create(nil); try Query.Connection := TimerConnection; Query.SQL.Text := 'select client.entreprise, RDV.date_ntf from client, Rdv where client.num_c = rdv.num_c and rdv.Date_Ntf = date(''now'')'; while Checking do begin // 等待30秒或收到停止信号 if CheckEvent.WaitFor(30000) = wrSignaled then Break; Today := Time; // 精准匹配小时和分钟 if (HourOf(Today) = HourOf(notiftime)) and (MinuteOf(Today) = MinuteOf(notiftime)) then begin NeedNotify := False; Query.Close; Query.Open; while not Query.Eof do begin if SameText(Query.FieldByName('date_ntf').AsString, FormatDateTime('yyyy-mm-dd', Now)) then begin NeedNotify := True; Break; end; Query.Next; end; if NeedNotify then begin // 切换到UI线程发送通知 TThread.Synchronize(nil, procedure var MyNotification: TNotification; begin MyNotification := TheNotifications.CreateNotification; try MyNotification.AlertBody := 'Le RDV avec ' + Query.FieldByName('entreprise').AsString + ' vous attend!'; TheNotifications.ScheduleNotification(MyNotification); finally MyNotification.DisposeOf; end; end); end; end; end; finally Query.Free; CheckEvent.Free; end; end); end;
四、额外优化建议
- 改用Android原生
AlarmManager:系统推荐用AlarmManager实现定时任务,比后台轮询更省电,还能避免系统杀死后台线程。Delphi可通过JNI调用其API。 - 调整服务启动模式:你设置的
START_REDELIVER_INTENT适合需确保任务完成的场景,若只是定时通知,改用START_STICKY更合适,服务被杀死后会自动重启。 - 数据库优化:使用FireDAC组件替代旧版TQuery,它支持线程安全操作和异步查询,稳定性更强。
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

