前台20s后台200s不执行玩就报ANR异常。
一、概念
没有界面在后台长期运行在主线程中的一个组件。
1.1 与线程的区别
Service | Thread |
可以配置执行在不同的进程中。 | CPU调度的最小单位。 |
任何有Context的地方都可以控制Service | 当Activity销毁后不再持有该Thread的引用,不管该子线程是一次任务还是循环任务都无法再控制。 |
1.2 两种方式开启服务的区别
start方式 | bind方式 | |
使用场景 | 启动一个后台服务长期执行某个任务。 | 生命周期和Activity绑定。外部需要与服务通讯,调用服务中的方法。公开接口供客户端远程调用,绑定时才会执行。 |
生命周期 | onCreate→onStartCommand→onDestroy | onCreate→onBind→onUnbind→onDestroy |
多次开启 | 每次都执行onStartCommand | 无效果 |
多次关闭 | 抛异常? | 抛异常 |
二、生命周期
所有和界面相关生命周期都没有。如果Service或者Activity是new出来的就是普通类不是组件了,这样调用里面的方法没意义。
2.1 onStartCommand()中的返回值
系统会因为内存不足而销毁Service,是可以等到内存充足后再重建Service,并执行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保留。 |
三、属性配置
<service
android:name=".MyService" //Service的类名
android:label //Service的名字,若不设置,默认为Service类名
android:icon //Service的图标
android:permission //申明此Service的权限,有提供了该权限的应用才能控制或连接此服务
android:process //表示该服务是否在另一个进程中运行(远程服务),不设置默认为本地服务;remote则设置成远程服务
android:foregroundServiceType="location|camera|microphone" //在前台Service中获取定位/摄像头/麦克风权限需要配置(Android10引入定位,11引入摄像头麦克风)
android:enabled="true" //是否默认启动,默认为 true
android:exported="true" //该服务是否能够被其他应用程序所控制或连接 不设置默认此项为 true
>
<intent-filter android:priority="1000" /> //配置优先级,最大1000
</service>
四、开启关闭
4.1 start 方式
- 长期后台运行(不会因为APP或者Activity销毁而停止,但服务进程在内存不足时会被回收),外部不能调用Service里的方法。
- 每调用一次startService(),onStartCommand()就会执行一次,但实际上每个 Service 只会存在一个实例。所以不管调用了多少次 startService(),只需调用一次 stopService() 或 stopSelf() ,Service就会停止。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.2 bind 方式
- 生命周期和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 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.3 混合方式
- 既长期在后台运行,又能调用 Service 里面的方法。
- 在 Activity 的 onCreate() 中 startService() 并 bindService(),顺序无所谓。
- 在 Activity 的 onDestroy() 中 unbindService(),根据情况在需要的地方stopService(),顺序无所谓两者都调用才会销毁服务。
- startService() 后,不管是否有 Activity 进行 bindService() 或 unbindService(),Service都在后台运行着,直到调用 stopService() 或 stopSelf() 才会关闭,或者系统资源不足时被杀死。
五、进程间通信
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开始,只有APP保持在前台可见状态的情况下 Service 才能保证稳定运行,一旦进入后台 Service 随时都有可能被系统回收,防止恶意APP长期在后台占用手机资源。因此需要 Service 能一直保持运行状态就可以使用前台Service。
- 前台Service在状态栏里会显示图标,下拉通知栏有显示通知。(这样APP就以另一种形式保持前台可见让用户清楚得知道什么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) //开启时设置的唯一标识
}
七、耗时任务 IntentService
Service默认运行在主线程,耗时操作需要开启子线程。Service一旦启动就会一直运行,直到调用 stopService()、stopSelf() 或被系统回收,我们可能会忘记调用。为了简单创建一个异步会自动停止的Service,可以使用IntentService。
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", "有自动停止")
}
}