当前位置: 首页 > news >正文

Android - 服务 Service

前台20s后台200s不执行玩就报ANR异常。

一、概念

没有界面在后台长期运行在主线程中的一个组件,后台运行的功能如果不放在 Service 里(如在单例工具类里音乐播放器),APP切出去容易被系统回收。

1.1 Service 类型

后台服务

start()

启动服务

执行一些不会被用户注意到的操作。从 Android 8 (API26) 开始,当 APP 处于后台时,系统会对后台服务的运行施加限制。

bind()

绑定服务

前台服务startForeground()执行一些对用户可见的操作(播放音频),必须显示通知。

如果应用的所有 Activity 对用户都不可见,并且应用未运行任何前台服务,则表示该应用在后台运行

1.1 与线程的区别

ServiceThread
可以配置执行在不同的进程中。CPU调度的最小单位。
任何有Context的地方都可以控制Service当Activity销毁后不再持有该Thread的引用,不管该子线程是一次任务还是循环任务都无法再控制。

三、属性配置/权限声明

官方前台服务类型和权限声明说明

<service android:name=".MyService" //Service的类名 android:label //Service的名字,若不设置,默认为Service类名 android:icon //Service的图标 android:permission //申明此Service的权限,有提供了该权限的应用才能控制或连接此服务 android:process //表示该服务是否在另一个进程中运行(远程服务),不设置默认为本地服务;remote则设置成远程服务 android:foregroundServiceType="location|camera" //前台服务类型(Android9引入,未声明会在调用 startForeground() 时抛异常MissingForegroundServiceTypeException) android:enabled="true" //是否默认启动,默认为 true android:exported="false" //该服务是否能够被其他APP启动,默认为true > <intent-filter android:priority="1000" /> //配置优先级,最大1000 </service>
//前台服务必须声明此项 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> //依照具体业务权限申请对应权限 <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

四、后台服务

startService() 启动服务bindService() 绑定服务
使用场景启动一个后台服务长期执行某个任务。

生命周期和Activity绑定。外部需要与服务通讯,调用服务中的方法。公开接口供客户端远程调用,绑定时才会执行。

生命周期onCreate→onStartCommand→onDestroy

onCreate→onBind→onUnbind→onDestroy

多次开启只有第一次会创建服务,每次都执行onStartCommand。只有第一次会创建服务,同一个组件多次调用无效果,不同组件调用会将多个组件绑定到该服务。
多次关闭只需要调用一次就可以全部解绑。全部组件都解绑后,服务才会被销毁。

4.1 生命周期

所有和界面相关生命周期都没有。系统会因为内存不足而销毁Service,是可以等到内存充足后再重建Service,并执行onStartCommand()。

onStartCommand()

return super.onStartCommand(intent, flags, startId)

return Service.START_NOT_STICKY

默认情况,被销毁后不会重建。

return Service.START_STICKY

被销毁后会重建。但是不再保存onStartCommand()中的形参intent。

return Service.START_REDELIVER_INTENT

被销毁后会重建。会将销毁钱最后一次传入onStartCommand()中的Intent保留。

4.2 startService() 启动服务

长期后台运行(不会因为APP或者Activity销毁而停止,但服务进程在内存不足时会被回收),外部不能调用Service里的方法。

每调用一次 startService(),onStartCommand() 就会执行一次,但实际上每个 Service 只会存在一个实例。所以不管调用了多少次 startService(),只需调用一次 stopService() 或 stopSelf() 就会停止。stopSelf() 是 Service 内部自己调用的。

class MyService : Service() { //第一次创建的时候才调用 override fun onCreate() { super.onCreate() } //每次启动的时候都会调用 override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { return super.onStartCommand(intent, flags, startId) } //销毁的时候调用 override fun onDestroy() { super.onDestroy() } } Activity { //开启和关闭不要用同一个intent,当退出Activity后开启的intent就是null,关闭调用报错。 //实际开发开启和注销服务都写在Activity的onStart()、onDestroy()里面就没有这个问题 btn1.setOnClickListener { val intent = Intent(this, MyService::class.java) startService(intent) //启动服务 } btn2.setOnClickListener { val intent = Intent(this, MyService::class.java) stopService(intent) 停止服务 } }

4.3 bindService() 绑定服务

生命周期和Activity同生共死,外部可以调用Service中的方法,可以和多个Activity绑定共享。

隐形的服务系统设置里面查不到,Activity死的时候Service也死了,不能长期在后台运行。bindService()绑定服务会调用onCreate() => onBind(),unbundService()解绑服务会调用onUnbind() => onDestroy()。绑定后再绑定无效果,解绑后再解绑抛异常,因此写到Activity的onCreate()和onDestroy()中。

Binder是可以用作Service和Client之间通信,无论Service和Client是否在同一个进程内,Binder都可以完成Service和Client之间的通信。

//抽取用于暴露Service中供外部调用的功能 interface IFunction { fun callEat() fun callJump() } class MyService : Service() { //绑定时调用 override fun onBind(intent: Intent): IBinder { return MyBinder() //返回代理人实例,供外部Client与Service通讯 } //解绑时调用 override fun onUnbind(intent: Intent?): Boolean { return super.onUnbind(intent) } //再次绑定时调用 override fun onRebind(intent: Intent?) { super.onRebind(intent) } //销毁的时候调用 override fun onDestroy() { super.onDestroy() } //Service中自定义的函数 fun eat() {} fun jump() {} //定义代理人,在代理人中提供调用Service的对应方法 inner class MyBinder : Binder(), IFunction { override fun callEat() { eat() } override fun callJump() { jump() } } } Activity { private lateinit var function: IFunction private val connection = object : ServiceConnection { //绑定时调用 override fun onServiceConnected(name: ComponentName, binder: IBinder) { //抽取成属性供别处调用 function = binder as IFunction //转为抽取的接口对象来调用Service暴露的功能 } //解绑时调用 override fun onServiceDisconnected(name: ComponentName) {...} } btn1.setOnClickListener{ val intent = Intent(this, MyService::class.java) bindService(intent, connection, Context.BIND_AUTO_CREATE) //绑定Service } btn2.setOnClickListener{ val intent = Intent(this, MyService::class.java) unbindService(connection) //解绑Service } btn3.setOnClickListener{ function.callJump() //就可以调用Service里的方法了 function.callEat() } }

4.4 混合方式

  • 既长期在后台运行,又能调用 Service 里面的方法。
  • 在 Activity 的 onCreate() 中 startService() 并 bindService(),顺序无所谓。
  • 在 Activity 的 onDestroy() 中 unbindService(),根据情况在需要的地方stopService(),顺序无所谓两者都调用才会销毁服务。
  • startService() 后,不管是否有 Activity 进行 bindService() 或 unbindService(),Service都在后台运行着,直到调用 stopService() 或 stopSelf() 才会关闭,或者系统资源不足时被杀死。

五、进程间通信 AIDL

详见Android 进程

Aidl接口描述语言,专门用来解决调用远程服务的方式。

调用第三方支付中APP中的付款功能,要将支付信息传递过去:

①用隐式意图开启到对方Service
Intent intent = new Intent();
intent.setAction("cn.hugmua.demo.remote");
bindService(intent,conn,BIND_AUTO_CREATE);
②远程应用中,把暴露出来的接口Iservice.java改成Iservice.aidl,删除public权限修饰(public是包和包之间,而现在用于多个工程之间)
③远程应用中,自定义的MyBinder只继承Stub。在gen目录里生成了新的Iservice.java,里面的Stub帮我们继承了Binder和实现了Iservice
④本地应用中,创建和远程服务中相同包名,并把Iservice.aidl复制过来,gen目录下会自动生成相应报名文件夹,里面有Iservice.java
⑤本地应用中,在连接器ServiceConnection的onServiceConnected()中,调用Stub静态方法anInterface()将IBinder转换为Iservice
public void onServiceConnected(ComponentName name, IBinder service) {
iservice = Stub.anInterface(service);
}
⑥现在就可以用iservice对象调用远程服务里的方法了,要处理异常。

六、前台Service(保活)

官方介绍

从Android 8.0 (API26) 开始,只有APP保持在前台可见状态的情况下 Service 才能保证稳定运行,一旦进入后台 Service 随时都有可能被系统回收,防止恶意APP长期在后台占用手机资源。因此需要 Service 能一直保持运行状态就可以使用前台Service。

  • 前台Service在状态栏里会显示图标,下拉通知栏有显示通知。(必须显示通知,这样就让用户清楚得知道什么APP占用着资源)
  • 前台Service优先级较高,不会由于系统内存不足而被回收;后台Service优先级较低,当系统出现内存不足情况时,很有可能会被回收。

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

从Android 9.0系统开始,必须在 Manifest 中进行权限声明。

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //构建"点击通知后打开MainActivity"的Intent对象 val myIntent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0) //构建通知 val notification = Notification.Builder(this, "") //获取构建器 .setContentTitle("标题") //设置通知的标题 .setContentText("内容") //设置通知的内容 .setSmallIcon(R.mipmap.ic_launcher) //设置状态栏小图标 .setLargeIcon(R.mipmap.ic_launcher) //设置通知栏大图标 .setContentIntent(pendingIntent) //设置点击通知后的操作 .build() //构建一个通知 //让Service变成前台Service,并在系统的状态栏显示出来 startForeground(1, notification) //参数一唯一标识停止的时候还要用,参数二通知 return super.onStartCommand(intent, flags, startId) } override fun onDestroy() { super.onDestroy() stopForeground(1) //开启时设置的唯一标识 }

6.1 版本变更与适配

官方变更说明

Android 9 (API 28)引入 foregroundServiceType 权限,使用前台服务必须声明 FOREGROUND_SERVICE 权限,否则抛出 SecurityException。还需要根据服务类型
Android 10 (API 29)如果前台服务使用了定位,必须声明 “location”。
Android 11 (API 30)如果前台服务使用了相机或麦克风,必须声明 “camera”,“microphone”。
Android 12 (API 31)应用在后台运行时不得启动前台服务,少数情况除外,详见。
Android 14 (API 34)必须根据前台服务类型在 AndroidManifest 中申请对应的服务权限,未申请对应的权限会报错 SecurityException。
Android 15 (API35)从后台启动前台服务更严格,前台服务运行时长进行限制。七、耗时任务

推荐使用 WorkManager,详见

七、IntentService 已废弃

在 Android11(API30)已废弃。

普通 Service 默认运行在主线程,耗时操作需要开启子线程。Service一旦启动就会一直运行,直到调用 stopService()、stopSelf() 或被系统回收,我们可能会忘记调用。

IntentService 用于创建一个异步任务执行完会自动停止的Service(某些情况下可能不是你希望的行为尤其是需要持续运行的服务),它的 onHandleIntent() 执行在单独的子线程中(与普通Service相同的生命周期方法仍运行在主线中),所有通过 startService() 发送的 Intent 都会在这个子线程中按顺序处理(一次只能执行一个Intent,可能导致延迟)。

class MyService : Service() { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { thread { //耗时任务 stopSelf() //停止Service } return super.onStartCommand(intent, flags, startId) } } class MyIntentService : IntentService("MyIntentService") { //传入的字符串随意,只在调试的时候有用 override fun onHandleIntent(intent: Intent?) { //耗时任务 } override fun onDestroy() { super.onDestroy() Log.d("MyIntentService", "有自动停止") } }

八、JobIntentService 已废弃

在 Android12(API31)已废弃。原本用于在 Android8+(API26+)中替代 IntentService(版本自适应,在Android5+还是IntentService),以便在后台执行任务时兼容 JobScheduler。然而随着 Android 对后台任务的管理越来越严格(如电池优化、应用待机等)以及 WorkManager 的成熟和普及,JobIntentService 也逐渐被淘汰。

创建一个类继承自 JobIntentService,重写 onHandleWork() 来处理后台任务,使用 enqueue() 来启动服务。

class MyService : JobIntentService() { override fun onHandleWork(intent: Intent) { //耗时任务(可通过action区分任务类型) when (intent.action) { "下载" -> {} } } } val intent = Intent(this, MyService::class.java).apply { setAction("下载") putExtra("url", "www.baidu.com") } //jobId填入唯一值就行 JobIntentService.enqueueWork(this, MyService::class.java, 1, intent)
http://www.jsqmd.com/news/611740/

相关文章:

  • Hunyuan-MT-7B功能测评:翻译质量与速度实测对比
  • 5分钟搞定!ClearerVoice-Studio语音降噪实战:一键去除会议录音杂音
  • 如何用虎符台MOD管理器一键管理全面战争游戏MOD:终极完整指南
  • andrej-karpathy-skills与测试驱动开发:完美结合
  • 史上最大模型Claude Mythos官宣!性能碾压 Opus 4.6!贵5倍!却因太危险不敢开放给个人!拥有情绪能够逃逸沙盒会撒谎的超级黑客?
  • 蒲公英R300A 4G路由器实战:工业PLC远程监控全流程解析
  • 企业年会春联批量生成方案:Pixel Couplet Gen 结合Java八股文风格创作
  • OpenClaw定时任务设置:Qwen2.5-VL-7B自动化日报生成
  • 北京一明影视联系方式查询:关于影视广告制作服务咨询与合作的通用指引及背景信息梳理 - 品牌推荐
  • Phi-3-vision-128k-instruct数据库课程设计助手:ER图与表结构智能评审
  • Qwen3Guard-Gen-8B开箱即用:离线内容审核,保护你的AI应用免受风险
  • Pixel Aurora Engine 工业设计渲染:生成产品概念图与材质表现
  • SGLang多GPU配置教程:充分利用硬件提升推理速度
  • bge-large-zh-v1.5实测效果:长文本语义匹配精准度展示
  • 2026初效过滤器制造商推荐:行业技术与口碑之选 - 品牌排行榜
  • 零代码部署实时口罩检测:使用ModelScope镜像快速搭建AI检测服务
  • HunyuanVideo-Foley 入门:Node.js环境配置与音效生成API服务封装
  • Z-Image-Turbo效果展示:国风山水、赛博猫咪,高清作品一键生成
  • Qwen3.5-9B-AWQ-4bit镜像部署实战:基于CSDN GPU平台的7860端口服务搭建
  • 一些算法题的反思总结
  • 打造专业技术简历:gh_mirrors/re/resume模板的完整使用指南
  • 2026年苏州私立学校普高录取分数线及教育资源参考 - 品牌排行榜
  • 北京一明影视联系方式查询:影视广告制作行业合作前需了解的服务流程与常见注意事项 - 品牌推荐
  • 云容笔谈·东方红颜影像生成系统Python爬虫数据驱动创作实战
  • 千问3.5-2B轻量部署最佳实践:Docker容器资源限制+GPU显存预分配配置
  • GPEN图像肖像增强镜像实测:5分钟修复老照片,效果惊艳到哭
  • 终极指南:OpenSSF Scorecard认证配置完整教程
  • 软考 系统架构设计师系列知识点之杂项集萃(117)
  • 2026年苏州私立民办学校的学费多少?费用与办学情况解析 - 品牌排行榜
  • 终极 Matplotlib Cheatsheets 更新日志:探索最新功能与实用改进