前台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:foregroundServiceTypelocation|camera //前台服务类型Android9引入未声明会在调用 startForeground() 时抛异常MissingForegroundServiceTypeException android:enabledtrue //是否默认启动默认为 true android:exportedfalse //该服务是否能够被其他APP启动默认为true intent-filter android:priority1000 / //配置优先级最大1000 /service//前台服务必须声明此项 uses-permission android:nameandroid.permission.FOREGROUND_SERVICE/ //依照具体业务权限申请对应权限 uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_CAMERA/四、后台服务startService() 启动服务bindService() 绑定服务使用场景启动一个后台服务长期执行某个任务。生命周期和Activity绑定。外部需要与服务通讯调用服务中的方法。公开接口供客户端远程调用绑定时才会执行。生命周期onCreate→onStartCommand→onDestroyonCreate→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中的付款功能要将支付信息传递过去①用隐式意图开启到对方ServiceIntent 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转换为Iservicepublic 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:nameandroid.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 已废弃在 Android11API30已废弃。普通 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 已废弃在 Android12API31已废弃。原本用于在 Android8API26中替代 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)