从Service到WorkManager

简介: 关于Service,想必大家都太熟悉了,今天我们就再回顾下它的使用、概念、区别、变更历史等等。

前言


关于Service,想必大家都太熟悉了,今天我们就再回顾下它的使用、概念、区别、变更历史等等。


概念和使用


Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件


两种启动方式:


  • startService() 生命周期为:onCreate() -> onStartCommand() -> onDestory()
  • bindService() 生命周期为:onCreate() -> onBind() -> onUnBind() -> onDestory()


其中要注意的是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可以在Activity被销毁,程序被关闭之后都可以继续运行。
  • 不提供界面的应用组件。这其实解释了后台的意义,Service的后台指的是不和界面交互,不依赖UI元素。


而且比较关键的点是,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


任务调度JobSchedulerAndroid5.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,然后根据版本需求等来执行这些任务,官网有一张图:


13.png


所以WorkManager到底能做什么呢?


  • 1、对于一些任务约束能很好的执行,比如网络、设备空闲状态、足够存储空间等条件下需要执行的任务。
  • 2、可以重复、一次性、稳定的执行任务。包括在设备重启之后都能继续任务。
  • 3、可以定义不同工作任务的衔接关系。比如设定一个任务接着一个任务。


总之,它是后台执行任务的一大利器。


参考


https://developer.android.google.cn/guide/components/services#Lifecycle 

http://gityuan.com/2017/03/10/job_scheduler_service/

目录
相关文章
|
Android开发
Android基础知识:什么是Fragment?与Activity的区别是什么?
Android基础知识:什么是Fragment?与Activity的区别是什么?
2483 54
|
Web App开发 缓存 JavaScript
一次性完整学完搭建PWA项目
一次性完整学完搭建PWA项目
649 0
|
存储 缓存 Java
Android Binder机制原理(史上最强理解,没有之一)
原文地址: http://blog.csdn.net/universus/article/details/6211589   Binder是Android系统进程间通信(IPC)方式之一。
3147 0
|
存储 安全 Go
Golang深入浅出之-原子操作包(sync/atomic)在Go中的应用
【4月更文挑战第23天】Go语言的`sync/atomic`包支持原子操作,防止多线程环境中的数据竞争。包括原子整数和指针操作,以及原子标量函数。常见问题包括误用非原子操作、误解原子操作语义和忽略内存排序约束。解决方法是使用原子函数、结合其他同步原语和遵循内存约束。注意始终使用原子操作处理共享变量,理解其语义限制,并熟悉内存排序约束,以实现并发安全和高效的应用程序。
209 1
|
关系型数据库 MySQL 大数据
MySQL索引失效的原因及应对策略
引言 在大数据时代,数据库管理成为了每个开发者必备的技能之一。MySQL作为全球最受欢迎的开源关系数据库管理系统,深受开发者们的喜爱。为了提高查询效率,我们常常会在MySQL中使用索引,但有时候你会发现,索引并没有发挥出预期的作用,原来,它已经“失效”了。本文将带你探索MySQL中索引失效的原因以及如何避免,让我们一同跨入数据库的奇妙世界。
1464 0
|
机器学习/深度学习 算法 数据可视化
使用Optuna进行PyTorch模型的超参数调优
Optuna是一个开源的超参数优化框架,Optuna与框架无关,可以在任何机器学习或深度学习框架中使用它。本文将以表格数据为例,使用Optuna对PyTorch模型进行超参数调优。
364 0
|
JavaScript 前端开发 安全
什么是TS?
TS(TypeScript)是一种由微软开发的编程语言,它是 JavaScript 的一个超集,提供了静态类型检查、类、接口、泛型等特性,可以在大型项目中提供更好的代码可读性、可维护性和可靠性。
钱大妈基于 Flink 的实时风控实践
钱大妈与阿里云 Flink 实时计算团队共建实时风控规则引擎,精确识别羊毛党以防营销预算流失。
钱大妈基于 Flink 的实时风控实践
|
Java Android开发 开发者
《阿里巴巴Android开发手册》电子版地址
本手册以开发者为中心视角分为Java语言规范,Android资源文件命名与使用,Android基本组件,UI与布局等九大部分。
912 0
《阿里巴巴Android开发手册》电子版地址
|
存储 自然语言处理 前端开发
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?
1557 0
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?