前言
关于Service,想必大家都太熟悉了,今天我们就再回顾下它的使用、概念、区别、变更历史等等。
概念和使用
Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件
两种启动方式:
其中要注意的是onStartCommand方法的返回值,有三种常量:
1) START_NOT_STICKY,终止服务后,除非除非有待传递的挂起 Intent,否则系统不会重建服务。
2) START_STICKY,终止服务后,会自动重新服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。
3) START_REDELIVER_INTENT,终止服务后,会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。
当然,最后要使用的话还要在清单文件中注册:
- <service android:enabled=["true" | "false"]
- android:exported=["true" | "false"]
- android:icon="drawable resource"
- android:isolatedProcess=["true" | "false"]
- android:label="string resource"
- android:name="string"
- android:permission="string"
- android:process="string" >
- . . .
- </service>
Service与子线程
关于Service,我的第一反应是运行在后台的服务。
关于后台,我的第一反应又是子线程。
那么Service和子线程到底是什么关系呢?
Service有两个比较重要的元素:
而且比较关键的点是,Service也是运行在主线程之中。
所以运行在后台的Service和运行在后台的线程区别还是挺大的。
当然,虽然两者概念不同,但是还是有很多合作之处。
Service作为后台运行的组件,其实很多时候也会被用来做耗时操作,那运行在主线程的Service肯定不能直接进行耗时操作,这就需要子线程了。
开启一个后台Service,然后在Service里面进行子线程操作,这样的结合给项目带来的可能性就更大了。
Google也是考虑到这一点,设计出了IntentService这种已经结合好的组件供我们使用。
IntentService
IntentService 是一个继承自Service,自带工作线程和Handler,并且线程任务结束后自动销毁的一个类。
源码很简单:
- @Override
- public void onCreate() {
- super.onCreate();
- //创建新线程并start
- HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
- thread.start();
- mServiceLooper = thread.getLooper();
- //创建新线程对应的handler
- mServiceHandler = new ServiceHandler(mServiceLooper);
- }
- @Override
- public void onStart(@Nullable Intent intent, int startId) {
- //service启动后发送消息给handler
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- msg.obj = intent;
- mServiceHandler.sendMessage(msg);
- }
- private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- //handler收到消息后调用onHandleIntent方法
- onHandleIntent((Intent)msg.obj);
- stopSelf(msg.arg1);
- }
- }
弊端
之前也说了,Service这些特性确实给了我们更多的可能性,我们可以在后台静默下载项目需要的东西、可以发心跳包、可以处理一些数据。
但是,也正是因为后台无感知的特性,也带来了隐私方面的隐患和弊端。
App可以在后台操作用户数据,下载应用无关的文件等等。
所以Google为了保护用户隐私,在Android8.0开始,限制了后台Service。
后台和前台Service
这就涉及到Service的分类了。
如果从是否无感知来分类,Service可以分为前台和后台。前台Service会通过通知的方式让用户感知到,后台有这么一个玩意在运行。
比如音乐类APP,在后台播放音乐的同时,可以发现始终有一个通知显示在前台,让用户知道,后台有一个这么音乐相关的服务。
在Android8.0,Google要求如果程序在后台,那么就不能创建后台服务,已经开启的后台服务会在一定时间后被停止。
所以,建议使用前台Service,它拥有更高的优先级,不易被销毁。使用方法如下:
- startForegroundService(intent);
- public void onCreate() {
- super.onCreate();
- Notification notification = new Notification.Builder(this)
- .setChannelId(CHANNEL_ID)
- .setContentTitle("主服务")//标题
- .setContentText("运行中...")//内容
- .setSmallIcon(R.mipmap.ic_launcher)
- .build();
- startForeground(1,notification);
- }
- <!--android 9.0上使用前台服务,需要添加权限-->
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
那后台任务该怎么办呢?官方建议使用 JobScheduler 。
JobScheduler
任务调度JobScheduler,Android5.0被推出。(可能有的朋友感觉比较陌生,其实他也是通过Service实现的,这个待会再说)
它能做的工作就是可以在你所规定的要求下进行自动任务执行。比如规定时间、网络为WIFI情况、设备空闲、充电时等各种情况下后台自动运行。
所以Google让它来替代后台Service的一部分功能,使用:
首先,创建一个JobService:
- public class MyJobService extends JobService {
- @Override
- public boolean onStartJob(JobParameters params) {
- return false;
- }
- @Override
- public boolean onStopJob(JobParameters params) {
- return false;
- }
- }
然后,注册这个服务(因为JobService也是Service)
- <service android:name=".MyJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
最后,创建一个JobInfo并执行
- JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
- ComponentName jobService = new ComponentName(this, MyJobService.class);
- JobInfo jobInfo = new JobInfo.Builder(ID, jobService)
- .setMinimumLatency(5000)// 任务最少延迟时间
- .setOverrideDeadline(60000)// 任务deadline,当到期没达到指定条件也会开始执行
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 网络条件,默认值NETWORK_TYPE_NONE
- .setRequiresCharging(true)// 是否充电
- .setRequiresDeviceIdle(false)// 设备是否空闲
- .setPersisted(true) //设备重启后是否继续执行
- .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) //设置退避/重试策略
- .build();
- scheduler.schedule(jobInfo);
简单说下原理:
JobSchedulerService是在SystemServer中启动的服务,然后会遍历没有完成的任务,通过Binder找到对应的JobService,执行onStartJob方法,完成任务。具体可以看看参考链接的分析。
所以也就知道了,在5.0之后,如果有需要后台任务执行,特别是需要满足一定条件触发的任务,比如网络电量等等情况,就可以使用JobScheduler。
有的人可能要问了,5.0之前怎么办呢?
可以使用GcmNetworkManager或者BroadcastReceiver等处理部分情况下的任务需求。
Google也是考虑到了这一点,所以将5.0之后的JobScheduler和5.0之前的GcmNetworkManager、GcmNetworkManager、AlarmManager等和任务相关的API相结合,设计出了WorkManager。
WorkManager
WorkManager 是一个 API,可供您轻松调度那些即使在退出应用或重启设备后仍应运行的可延期异步任务。
作为Jetpack的一员,并不算很新的内容,它的本质就是结合已有的任务调度相关的API,然后根据版本需求等来执行这些任务,官网有一张图:
所以WorkManager到底能做什么呢?
总之,它是后台执行任务的一大利器。
参考
https://developer.android.google.cn/guide/components/services#Lifecycle
http://gityuan.com/2017/03/10/job_scheduler_service/
本文转载自微信公众号「码上积木」,作者码上积木。转载本文请联系码上积木公众号。
本文转载自公众号读芯术(ID:AI_Discovery)。 数字化将DataOps这一概念引入人们...
独立 域名 在哪里备案? 域名备案 是到购买 网站空间 的服务商处进行的,也就是...
一、软件架构设计的生命周期 1. 软件开发流程 2. 关于套路 3. 先僵化,后优化,...
操作场景 本节为您介绍通过控制台提供的CloudShell登录云服务器的操作步骤。 登...
哪个服务器不需要备案?服务器是否备案,取决于购买什么地区的服务器。备案是我...
一、本篇知识脉络 以 Java 举例,线程的使用过程中遇到了什么痛点?池化的思想的...
各位开发者们 有没有遇到过算法才思泉涌但无数据验证的困境 或是论文方向确定但...
哪里可以免费申请二级 域名 ?申请免费的二级域名,通常先有顶级域名(一级域名...
认识 kafka kafka简介 Kafka 是一个分布式流媒体平台,kafka官网:http://kafka....
大家好,我是前端进阶者。 polygon元素定义了一个由一组首尾相连的直线线段构成...