Delphi Android Service 后台保活实战:前台服务与绑定通信完整Demo
简介这份Delphi Android Service Demo面向使用Delphi进行安卓开发的程序员尤其是希望掌握后台服务机制的FireMonkey跨平台开发者。资源以XE5与XE7两个版本分别给出示例帮助理解Android四大组件中Service的创建、注册与启动绑定流程解决后台任务持久运行、服务与Activity通信等实际问题。压缩包共32个文件约69KB包含10个pas源码、6个java文件、2个dproj工程、2个dpr主程序、2个fmx界面、2个xml配置及bat脚本、jar包等覆盖服务类、主Activity、AndroidManifest声明与构建脚本等模块。已有286人学习。通过研读可掌握TAndroidService子类实现、OnCreate与OnStartCommand等生命周期方法、startService与bindService调用方式以及FireMonkey下的多平台兼容处理为开发需在后台持续工作的Delphi安卓应用提供可复用的参考范例。1. 后台保活的硬骨头Delphi Android Service Demo 到底能解决什么做过 Android 后台保活的人都知道这是个玄学问题。应用切到后台系统随时可能把进程回收推送收不到、定位断了、定时任务停了。用 Delphi 做 Android 开发的朋友更头疼——FMX 框架本身对 Service 的封装不算厚网上能找到的完整 Demo 少之又少大部分帖子只贴几行代码就没了下文。这份 Delphi Android Service Demo 就是冲着这个痛点来的它演示了如何在 Delphi 环境下创建、启动、绑定一个 Android 前台 Service让后台逻辑在应用退到后台后依然能跑。适合谁适合已经能用 Delphi 编译出 Android APK、但对 Android 四大组件里的 Service 层不太熟、又不想切到 Android Studio 重写一遍的中级开发者。它不教你 Delphi 语法也不教你 Android 入门它解决的是“Delphi 写的 App 怎么在 Android 上有个正经后台服务”这个具体问题。2. Delphi 里写 Service从 JNI 桥接到生命周期回调2.1 为什么 Delphi 的 Service 不能照抄 Java 写法Android 原生开发里Service 是一个继承自android.app.Service的 Java 类重写onCreate、onStartCommand、onBind、onDestroy这几个回调。Delphi 编译出来的是原生 ARM 代码没有 JVM 字节码所以不能直接“继承” Java 的 Service 类。Delphi 的做法是通过 JNIJava Native Interface桥接在 Java 侧写一个壳 Service把生命周期回调转发给 Delphi 侧的 native 方法。这个壳通常是一个极简的 Java 类编译成 jar 或 aar 后随 APK 打包进去。常见做法是在 Delphi 工程里放一个AndroidService.java里面定义好onCreate等回调每个回调里调用一个native声明的方法。Delphi 侧用JNI单元注册这些 native 方法的实现。这样 Java 壳负责跟 Android 系统打交道Delphi 负责真正的业务逻辑。听起来绕但这是目前 Delphi 做 Android Service 最稳的路子没有之一。提示如果你的 Delphi 版本较新IDE 里可能自带 Service 模板但模板生成的代码往往只覆盖了最基础的前台 Service绑定和跨进程通信还得自己补。2.2 前台 Service 的创建与通知渠道适配Android 8.0API 26之后后台 Service 受到严格限制想长时间运行必须走前台 Service并且要显示一个常驻通知。Android 13API 33又引入了POST_NOTIFICATIONS运行时权限。这份 Demo 里对通知渠道的创建做了适配核心逻辑在 Delphi 侧调用 Android 的NotificationManager和NotificationChannel。下面是一段 Delphi 侧创建通知渠道的代码示例// 创建通知渠道适配 Android 8.0 procedure TMainForm.CreateNotificationChannel; var Channel: JNotificationChannel; Manager: JNotificationManager; begin if TOSVersion.Check(8, 0) then begin // 渠道 ID 和名称名称会显示在系统设置里 Channel : TJNotificationChannel.JavaClass.init( StringToJString(delphi_service_channel), StringToJString(后台服务), TJNotificationManager.JavaClass.IMPORTANCE_LOW // 低重要性不响铃 ); Channel.setDescription(StringToJString(保持后台任务运行)); Manager : TJNotificationManager.Wrap( TAndroidHelper.Context.getSystemService( TJContext.JavaClass.NOTIFICATION_SERVICE ) ); Manager.createNotificationChannel(Channel); end; end;这段代码的逻辑说明先判断系统版本是否达到 8.0因为NotificationChannel是 API 26 才有的类。渠道 ID 是自定义字符串后续发通知时要对应上。IMPORTANCE_LOW表示通知不发出声音、不弹横幅适合后台服务这种“存在感越低越好”的场景。TAndroidHelper.Context是 Delphi 提供的获取当前 Context 的便捷方式比手动传参省事。参数方面渠道重要性有五个等级IMPORTANCE_NONE、IMPORTANCE_MIN、IMPORTANCE_LOW、IMPORTANCE_DEFAULT、IMPORTANCE_HIGH。前台 Service 一般用LOW或MIN用HIGH会被用户嫌弃。渠道创建后不能修改重要性只能删了重建所以第一次就要选对。2.3 启动、绑定与跨进程通信的代码骨架Service 的启动方式有两种startService和bindService。前者让 Service 独立运行后者让调用方和 Service 建立连接、可以调用其方法。Demo 里两种都演示了。启动的代码在 Delphi 侧通过JIntent发起// 启动前台 Service procedure TMainForm.StartMyService; var Intent: JIntent; begin Intent : TJIntent.JavaClass.init( TAndroidHelper.Context, TJDelphiService.JavaClass // Java 壳 Service 的类引用 ); Intent.putExtra(StringToJString(command), StringToJString(start)); if TOSVersion.Check(8, 0) then TAndroidHelper.Context.startForegroundService(Intent) else TAndroidHelper.Context.startService(Intent); end;逻辑说明JIntent指定要启动的 Service 类putExtra可以传参。Android 8.0 以上必须用startForegroundService否则会抛IllegalStateException。Service 启动后必须在 5 秒内调用startForeground显示通知否则系统会 ANR 并杀掉 Service。这个 5 秒窗口是硬性约束Delphi 侧的业务初始化如果太慢要把耗时操作放到子线程里。绑定方式则用bindService传入一个ServiceConnection的实现。Delphi 里实现 Java 接口比较繁琐通常用TJServiceConnection封装。绑定成功后可以通过IBinder拿到 Service 实例直接调用其 Pascal 方法。跨进程通信IPC如果涉及独立进程需要用 AIDL但 Demo 里是同一进程所以直接强转即可。注意startForeground的通知 ID 不能为 0否则通知不显示Service 也会被判定为未进入前台状态。3. 把 Demo 跑起来编译配置与真机调试的完整链路3.1 工程结构与关键文件清单拿到 Demo 后先别急着编译。Delphi 的 Android 工程对文件位置和引用路径比较敏感目录结构乱了就报一堆找不到符号。典型的工程结构如下文件/目录作用是否必须Project1.dprDelphi 工程主文件是Unit1.pas主界面逻辑是AndroidService.javaJava 壳 Service是AndroidManifest.template.xml清单模板声明 Service 和权限是libs/存放编译好的 jar 包视情况res/图标、布局等资源否AndroidManifest.template.xml是重点。Delphi 编译时会用这个模板生成最终的AndroidManifest.xml。Service 的声明、前台服务权限、通知权限都要写进去!-- 在 application 节点内声明 Service -- service android:namecom.embarcadero.services.DelphiService android:enabledtrue android:exportedfalse android:foregroundServiceTypedataSync / !-- 权限声明 -- uses-permission android:nameandroid.permission.FOREGROUND_SERVICE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_DATA_SYNC / uses-permission android:nameandroid.permission.POST_NOTIFICATIONS /foregroundServiceType是 Android 10 之后的要求必须声明具体类型否则startForeground会失败。dataSync适合数据同步类任务如果是定位就用location媒体播放用mediaPlayback。类型选错会被系统拒绝这个坑后面还会细说。3.2 编译参数与 SDK 版本对齐Delphi 的 Android 编译目标 SDK 版本直接影响 Service 的行为。在Project Options→Application→Android里Target SDK Version建议设成 33 或 34Min SDK Version至少 21。但注意Target SDK 越高系统对后台限制越严。如果设成 34前台 Service 的类型声明和权限必须完整否则一启动就崩。编译时还有一个容易翻车的地方Java 壳的编译。如果AndroidService.java没有预先编译成 jar 并放到libs/下Delphi 编译不会报错但运行时找不到类直接ClassNotFoundException。常见做法是用javac配合android.jar手动编译# 编译 Java 壳为 class 文件 javac -source 1.8 -target 1.8 \ -bootclasspath %ANDROID_SDK%\platforms\android-33\android.jar \ -d out \ AndroidService.java # 打包成 jar jar cvf DelphiService.jar -C out .然后把DelphiService.jar放到工程的libs/目录并在Project Options→Android→Libraries里添加引用。这一步不做后面所有调试都是白费。3.3 真机部署与日志抓取模拟器对 Service 的保活行为跟真机差别很大建议直接上真机。部署时用 USB 调试Delphi IDE 里选好目标设备点 Run。如果 Service 启动失败第一手信息在 Logcat 里。用adb logcat过滤adb logcat | findstr DelphiService AndroidRuntimeWindows 下用findstrmacOS/Linux 用grep。重点看AndroidRuntime标签下的FATAL EXCEPTION里面会写明是权限问题、类找不到还是前台通知超时。Delphi 侧的异常不会直接打到 Logcat需要在代码里用try..except捕获后手动Log.d输出。提示真机上第一次启动前台 Service 时系统可能弹出通知权限请求。如果用户拒绝Service 仍然能跑但通知不显示Android 会认为你没有正确进入前台状态可能在几分钟后杀掉。所以权限请求要处理好。4. 避坑指南Service 启动失败与保活失效的排查清单4.1 坑一startForeground 报 “not allowed to start service”现象应用在后台时调用startForegroundServiceLogcat 报IllegalStateException: Not allowed to start service Intent。原因Android 8.0 之后应用在后台时不能随意启动后台 Service必须用startForegroundService且 Service 必须在 5 秒内调用startForeground。如果 Delphi 侧在onStartCommand里做了耗时初始化超过了 5 秒系统就会抛这个异常。解决把耗时操作移到子线程onStartCommand里只做startForeground和轻量初始化。4.2 坑二通知渠道未创建导致通知不显示现象startForeground调用成功但状态栏没有通知Service 过一会儿被系统回收。原因Android 8.0 以上如果通知没有指定有效的渠道 ID或者渠道未创建通知会被静默丢弃。Delphi 侧如果只在 Java 壳里创建渠道而 Java 壳的代码没被正确编译进去渠道就是空的。解决在 Delphi 侧也做一次渠道创建兜底或者确认 Java 壳的 jar 已正确打包。用adb shell dumpsys notification可以查看当前应用的渠道列表。4.3 坑三foregroundServiceType 声明缺失或选错现象Target SDK 设成 34 后startForeground直接抛SecurityException或InvalidForegroundServiceTypeException。原因Android 14 要求前台 Service 必须声明foregroundServiceType且类型必须与startForeground时传入的类型一致。如果清单里写的是dataSync代码里传的是location就会失败。解决清单和代码里的类型必须严格对应。数据同步用FOREGROUND_SERVICE_TYPE_DATA_SYNC定位用LOCATION媒体用MEDIA_PLAYBACK。4.4 坑四Delphi 主线程阻塞导致 ANR现象Service 启动后界面卡死几秒后弹出 “应用无响应”。原因Delphi 的onStartCommand回调默认在主线程执行如果里面直接跑了数据库查询、网络请求等耗时操作主线程被占住系统判定 ANR。解决在onStartCommand里用TThread.CreateAnonymousThread或TTask把耗时逻辑丢到后台线程主线程只负责通知和状态标记。4.5 坑五应用被划掉后 Service 跟着死现象用户从最近任务列表划掉应用Service 也停了。原因这是 Android 的默认行为划掉任务栈会杀掉关联的 Service除非 Service 设置了android:stopWithTaskfalse。解决在清单的service节点加上android:stopWithTaskfalse这样划掉任务时 Service 不受影响。但注意这不能保证 Service 永远活着系统内存紧张时照样杀。5. 进阶技巧用绑定服务做双向通信与状态回传5.1 绑定服务的连接管理与回调接口启动式 Service 只能单向发命令拿不到实时状态。绑定式 Service 可以建立ServiceConnection拿到IBinder后直接调用 Service 的方法也能让 Service 通过回调接口把状态推给界面。Delphi 里实现这个模式关键是把 Java 的ServiceConnection接口用 Delphi 类实现并在onServiceConnected里保存IBinder引用。// Delphi 侧实现 ServiceConnection type TMyServiceConnection class(TJavaLocal, JServiceConnection) public procedure onServiceConnected(name: JComponentName; service: JIBinder); cdecl; procedure onServiceDisconnected(name: JComponentName); cdecl; end; procedure TMyServiceConnection.onServiceConnected(name: JComponentName; service: JIBinder); begin // 保存 IBinder后续通过它调用 Service 方法 FServiceBinder : TJDelphiServiceBinder.Wrap(service); FServiceBinder.setCallback(Self); // 注册回调Service 状态变化时通知界面 end;逻辑说明TJavaLocal是 Delphi 里实现 Java 接口的基类cdecl调用约定不能少。onServiceConnected里拿到的JIBinder是 Java 壳 Service 返回的 Binder 对象强转成自定义的 Binder 类后就能调用其 Pascal 方法。setCallback是自定义方法把 Delphi 对象传给 ServiceService 在状态变化时回调这个对象的方法。这样就实现了双向通信。参数方面bindService的flags常用BIND_AUTO_CREATE表示绑定不存在时自动创建 Service。如果希望绑定优先级更高可以加BIND_ABOVE_CLIENT但一般不需要。5.2 状态回传与线程安全Service 在后台线程跑任务状态回传到界面时必须切回主线程否则 Delphi 的 UI 控件会报跨线程访问错误。常见做法是用TThread.Queue或TThread.Synchronize// Service 状态变化时回调切回主线程更新 UI procedure TMyServiceConnection.OnStatusChanged(const AStatus: string); begin TThread.Queue(nil, procedure begin LabelStatus.Text : AStatus; end ); end;TThread.Queue是异步的不阻塞 Service 线程TThread.Synchronize是同步的会等主线程执行完。状态回传这种场景用Queue更合适避免 Service 被界面卡住。如果回传频率很高比如每秒好几次要在 Service 侧做节流不然主线程消息队列会被塞满界面反而更卡。5.3 验证 Service 是否真正在后台运行写完代码别只看界面要用系统工具验证。adb shell dumpsys activity services可以列出当前所有运行中的 Service找到你的包名和 Service 类名看isForeground是否为 true。adb shell dumpsys notification看通知是否挂载。还可以用adb shell am kill模拟系统杀进程观察 Service 是否按预期重启。我一般会在onStartCommand里返回START_STICKY这样 Service 被杀后系统会尝试重建但重建时Intent可能为 null代码里要做空判断这个坑我踩过不止一次。从那以后我每次写完 Service 都会强制走一遍真机后台跑 10 分钟、划掉任务栈、锁屏、再解锁看通知还在不在、日志有没有断。这套流程能过滤掉九成的保活问题。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

DNS解析调整实战:服务器IP变更的完整指南

DNS解析调整实战:服务器IP变更的完整指南

1. 项目概述网站服务器IP变更后的域名解析调整,是每个运维人员和网站管理者都会遇到的常规操作。看似简单的几个步骤背后,却隐藏着许多影响网站可用性的关键细节。去年我们公司数据中心迁移时,就曾因为DNS缓存问题导致新服务器上线后长达6小时…

2026/10/14 12:50:53 阅读更多 →
YOLO红细胞检测数据集:VOC/COCO标签转换与训练实战解析

YOLO红细胞检测数据集:VOC/COCO标签转换与训练实战解析

简介:红细胞目标检测数据集是一套面向医学图像分析与目标检测学习者的真实场景数据资源,覆盖多种红细胞成像条件,适合用于YOLO系列模型的训练、验证及算法效果对比。压缩包共2000个文件,以1986个xml标注文件为主,并附有…

2026/10/14 12:50:53 阅读更多 →
多Agent协作下的Skill管理:注册中心与包管理两种开源方案对比

多Agent协作下的Skill管理:注册中心与包管理两种开源方案对比

1. 多 Agent 环境下的 Skill 管理困局 1.1 一个 Skill 存三份,到底是怎么发生的 先说一个我亲身踩过的坑。去年下半年,我手上同时跑着三个 Agent 工作流:一个负责代码审查,一个负责文档生成,还有一个做数据清洗。三个…

2026/10/14 12:49:52 阅读更多 →

最新新闻

快速阅读不靠眼快:筛选与理解的实用框架

快速阅读不靠眼快:筛选与理解的实用框架

经常有人问我:“你读书怎么那么快?是不是一目十行、走马观花?”说实话,我刚开始试水快速阅读的时候也踩过不少坑,最典型的表现是字都看完了,合上书一问三不知。后来我花了很长时间去拆解“快速阅读”和“快…

2026/10/14 18:20:44 阅读更多 →
存量市场服务升级:低风险高回报的商业模式

存量市场服务升级:低风险高回报的商业模式

1. 为什么说"稳妥生意"比创业更值得关注最近三年,我身边有37位朋友尝试创业,最终存活下来的只有3家。这个残酷的数字让我开始思考:对于大多数普通人来说,是否存在着比创业风险更低、回报更稳定的商业机会?经…

2026/10/14 18:20:44 阅读更多 →
Decepticon 实战手册:EtherNet/IP + CIP 协议攻击全流程(44818/2222 端口下的 PLC 侦察、标签读写与模式切换)

Decepticon 实战手册:EtherNet/IP + CIP 协议攻击全流程(44818/2222 端口下的 PLC 侦察、标签读写与模式切换)

【免费下载链接】Decepticon Autonomous Hacking Agent for Red Team 项目地址: https://gitcode.com/gh_mirrors/de/Decepticon 点击查看 免费下载 本指南基于 Decepticon 仓库中 enip-cip 技能文档 编写,系统讲解针对北美工业控制系统(ICS…

2026/10/14 18:20:44 阅读更多 →
中点画圆算法详解:光栅化生成完美圆的数学原理与代码实现

中点画圆算法详解:光栅化生成完美圆的数学原理与代码实现

圆的生成算法,是计算机图形学课程里最容易被低估的一个实验。很多同学看到“画圆”两个字,第一反应是直接调用窗口库里的画圆函数,几行代码就结束。但实验真正要你做的,是把它当成一道光栅化算法题:输入圆心坐标、半径…

2026/10/14 18:20:44 阅读更多 →
把“学术导航仪”装进书匠策AI:一篇期刊论文的“造路”逻辑

把“学术导航仪”装进书匠策AI:一篇期刊论文的“造路”逻辑

官网直达:www.shujiangce.com 微信公众号搜一搜:书匠策AI 写期刊论文最折磨人的,从来不是“写”这个动作本身。 是你盯着空白文档,知道要做文献综述,却不知道从哪篇开始;知道要有理论框架,却拿…

2026/10/14 18:20:44 阅读更多 →
插件加载失败排查:从生命周期到激活机制,一篇搞定

插件加载失败排查:从生命周期到激活机制,一篇搞定

先说一句,我带过好几个插件化改造的项目,也修过无数跟插件相关的启动报错,所以看到 "failed to load plugins web boot: 2 entries did not activate" 这种日志时,第一反应不是翻框架文档,而是看插件的激活方…

2026/10/14 18:19:43 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →