android workmanager

目录

  • google后台任务推荐方案
  • doze简介
  • 从jobservice 到android-job 到workmanager
  • 遇到的一些坑
  • 总结

先看一下google推荐的后台任务的解决方案

image.png

如图后台任务可以从三个维度去考虑:

  • 是否需要立即执行
  • 是否需要执行完毕
  • 是否需要监听系统的状态 如网络状态等

然后给出了对应的具体的推荐方案

线程池、广播、前台任务这里暂时不提,主要看一下新出的workmanager,从图中可以看出workmanager的使用条件任务可以适当延迟的,即task没有闹钟那种强时效性。

workmanager是今年google io提出来的,目前版本还是1.0.0-alpha01,看见alpha就有点慌,后面果然出现bug了,这个后面再细说。

背景

为什么要使用workmanager,是alrammanager不好用吗?
其实这两者的作用并不完全一样;alarmmanager适用于类似闹钟那样必须准时唤醒的任务,不管是否处于doze(低耗电和应用待机模式);不过在6.0后的doze里面,alarmmanager需要调用setAndAllowWhileIdle()
setExactAndAllowWhileIdle()
如果必须要准时执行的任务比如股票每日开市提醒只能强行唤醒系统,但是也有的是不是要求时间这么准确的,比如每隔2天弹一个notification提示打开app啥的,这种实效性不那么高的就可以换成jobservice。

简单介绍一下doze

相关文档

image.png

这个是android6.0后出来的,6.0+的设备上都有这个,目的是为了省电。简单的说就是手机系统发现一段时间用户都没有在用手机了,就会进入省电模式,期间不会处理alarm、网络状态等,等省电了一段时间后再给一小段时间让所有应用统一处理alram等task,然后又进入省电状态。如果中途一直没有亮屏、充电等表示用户开始用手机了的操作,doze的省电周期会越来越长,如上图。
老实说,按照国人的习惯,白天手机想进入doze,基本上上不可能的。没有谁会好几个小时不碰手机吧。所以基本上都是晚上进入doze,这也说明了为啥早上亮屏的瞬间手机好多app会在同一时间弹出notification(亮屏就退出了doze,被延迟的alram马上开始工作)。

那么使用jobservice的优势在哪儿,我为什么不全部用alarmManager?

使用jobservice的优势就是doze下,不会唤醒系统,耗电量会减小。耗电量减小到一定程度,才能达到googleplay的推荐要求;在国内,两种类型差不多的app,用户通过电量消耗统计软件发现耗电量低的那个app,应该会对它更有好感吧(卸载也是先卸载竞争对手的app 哈哈)。

从jobservice 到android-job 到workmanager

前面说的时效性不是很强的任务可以用jobservice(JobScheduler),但是这个是android5.0+才出来的,要兼容5.0之前的,额外再用alramager;而且国外的还可以使用googleservice(Firebase JobDispatcher)实现。这就需要开发者自己来兼容;
后面发现印象笔记的android-job已经实现了类似的功能;
github上印象笔记的文档

androidjob的相关api

任务调度类型

  • 普通任务
    setExecutionWindow(start,end)
  • 特定时间执行的任务
    setExact(time)
  • 周期任务
    setPeriodic(long intervalMs, long flexMs)

任务的执行时间范围public Builder setExecutionWindow(long startInMs, long endInMs)

  • mStartMs
    Earliest point from which your task is eligible to run.

  • endInMs
    Latest point at which your task must be run. eligible to run.
    这两个是时间窗口的起始时间,意思是System.currentTimeMillis() + startInMs到System.currentTimeMillis() + endInMs之间执行,endInMs对应后面的deadline。
    在setPeriodic(long)和setExact(long)时不起作用

和周期任务相关的参数Builder setPeriodic(long intervalMs, long flexMs)

  • long mIntervalMs
    这个是周期执行有关的参数,每隔多久执行一次,最低是15分钟。

  • long mFlexMs
    这个是周期执行有关的参数,离周期末尾多久这个job应该被执行,最低5分钟。

    JobManager.instance().getConfig().setAllowSmallerIntervalsForMarshmallow(true); // Don't use this in production

    debug模式下设置这个,可以在api<24的机子上周期减小到60s,倒数减小到30s;正式环境,最低周期15分钟,倒数5分钟。

和任务执行失败,重新调度有关的参数public Builder setBackoffCriteria(long backoffMs, @NonNull BackoffPolicy backoffPolicy)

  • backoffMs
    非周期性的任务,执行失败后被重新调度需要等待的时间,配合着下面的策略使用。
    默认开始30s,逐渐增加最多5小时
  • backoffPolicy
    枚举,只有线性和指数增长两种方式。

一些二级条件吧,如果没有强制要求,会在deadline时忽略这些条件执行job

  • boolean mRequiresCharging
    是否需要插入设备(是指充电吧)
    默认false,但是如果之前的endInMS到了&&mRequirementsEnforced=false,就会忽略这个条件执行job

  • boolean mRequiresDeviceIdle
    是否需要等到设备空闲。同上面一样,当deadline到来&&没有mRequirementsEnforced=false时,就会忽略这个条件执行Job

  • boolean mRequiresBatteryNotLow
    是否禁止低电量。同上,deadline&&有mRequirementsEnforced=false会忽略。

  • boolean mRequiresStorageNotLow
    是否禁止低存储空间。这个只有在android o才有效果

  • NetworkType mNetworkType
    需要等待的网络状态,是netWorkType的枚举,有好几种,类似wify 、流量等,默认是不需要关心网络状态。
    同上条件。

  • boolean mRequirementsEnforced
    这个就是前面提到的那个参数,如果设为false,当job到deadline还没执行的时候,就可以抢救一下。

传参相关

  • PersistableBundleCompat mExtras
    带的额外参数,必须是这种类型,类似bundle。可以在onRunJob方法那里通过传过去的param取出来。

可以说android-job很良心了,暴露的api简单易用,功能强大。

workmanager的使用

本来已经使用android-job了,但是后面在github文档的后面看到了官方的说明:由于google出了workmanager,Android-job后面可能不再更新。然后又换到workmanager。

developer上workmanager的官方文档
使用起来还是很方便的,workmanager.enque(workRequest)就可以了,看起来类似google之前出的volley的风格,不知道是不是同一批程序员开发的哈哈。

public class DemoWorker extends Worker {
  public static final String TAG = "work_demo_tag";
  @NonNull @Override public WorkerResult doWork() {
    Data data = getInputData();
    int requestCode = data.getInt(AlarmMgr.ALARM_REQUEST_CODE, -1);
    String strData = data.getString(AlarmMgr.ALARM_NOTIFICATION_DATA, "");
    boolean bNotiClick = data.getBoolean(AlarmMgr.ALARM_NOTIFICATION_CLICK, false);
    Context context = getApplicationContext();
  //your job to do

  //可以再这里调用下一个一次性任务,形成周期循环。
 //现在默认执行成功,有需要再添加retry back-off相关逻辑
    return WorkerResult.SUCCESS;
  }
/**
   * @param requestCode requestCode
   * @param strData strData
   * @param bNotiClick bNotiClick
   */
  public static void schedule(final int requestCode, String strData, boolean bNotiClick) {
    WorkManager manager = WorkManager.getInstance();
   //下一次任务开始时,取消上一次相关tag类型的任务(避免网络状态不太好反复触发网络切换广播的情况)
   //这里如果queue里面存在100个以上job会crash,所以需要处理一下
    manager.cancelAllWorkByTag(String.valueOf(requestCode));
    OneTimeWorkRequest oneTimeWorkRequest =
        new OneTimeWorkRequest.Builder(AlarmReceiverWorker.class)
            .addTag(String.valueOf(requestCode))
            .setInputData(new Data.Builder()
                .putInt(AlarmMgr.ALARM_REQUEST_CODE, requestCode)
                .putString(AlarmMgr.ALARM_NOTIFICATION_DATA, strData)
                .putBoolean(AlarmMgr.ALARM_NOTIFICATION_CLICK, bNotiClick)
                .build())
            .build();
    manager.enqueue(oneTimeWorkRequest);
  }
}

需要注意的是队列里面任务(还在等待调度 未执行的那种)不能超过100个,不然会crash,这是workmanager代码的限制
workRequest子类有oneTime和periodic,对应一次性任务和周期性任务;因为基本上同样是对jobservice操作,所以方法很类似android-job,可以对照上面anroid-job用法或者查看developer文档workmanger简介
列一下常见用法

  • 多任务调度顺序
WorkManager.getInstance()
    // First, run all the A tasks (in parallel):
    .beginWith(workA1, workA2, workA3)
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in any order):
    .then(workC1, workC2)
    .enqueue();
  • 设置任务执行的约束条件
// Create a Constraints that defines when the task should run
Constraints myConstraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
    // Many other constraints are available, see the
    // Constraints.Builder reference
     .build();

// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest compressionWork =
                new OneTimeWorkRequest.Builder(CompressWorker.class)
     .setConstraints(myConstraints)
     .build();
  • 取消任务除了前面提到的cancleByTag,也可以用uniquework
 OneTimeWorkRequest oneTimeWorkRequest =
          new OneTimeWorkRequest.Builder(AlarmReceiverWorker.class).build();
      WorkManager.getInstance()
          .beginUniqueWork("downloadQueue", ExistingWorkPolicy.REPLACE, oneTimeWorkRequest);

说说坑吧

workermanager的周期任务,有时不能取消掉之前放到队列的任务。通过cancleByTag也不行,这个在stackoverflow和github上面的issue也看到有人提过,而且确实回复是个bug,应该是alpha版还是有些问题的。
既然周期任务有坑,我们也可以采用一次性任务开启下一个一次性任务,像链表那样。而且可以避开周期任务里面源码对周期设置最小15分钟的限制,不过一般也没那么流氓要隔几分钟就唤醒吧。
但是使用一次性任务循环触发,发现在小米上测试,打开app退到后台时,alarmmanager android-job workmanager正常,杀掉app,workmanager就不行了;使小米达到doze的条件,再亮屏,anroid-job也不行了。在小米上暂时只有alramanager适用(如果有哪位朋友发现小米上用workmanager有方法可以避开这个坑,请分享一下)。

还有就是前面提到的,要避开queue里面出现100个待调度的job的case。

总结

最后的做法定时任务使用alrammanager,等收到alram广播后交给worker处理;网络状态监听任务直接再接收到广播后交给worker处理。保证定时,但是收到广播放到队列里面不像之前那样接收到广播就串行马上执行了,等系统决定统一处理。

等后面workmanager版本号把alpha去掉就更好了;可以先用android-job,等workamanager出稳定版本再替换成worker,因为api太像了,替换的成本也不大。

这篇文章有些细节需要后面再进行补充。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,397评论 25 707
  • 1. WorkManager 1). 简介 其实就是"管理一些要在后台工作的任务, -- 即使你的应用没启动也能...
    _凌浩雨阅读 648评论 0 1
  • 一寸柔肠一缕丝,千梭万纫九张机。织就鸳鸯欲成对,明珠勿复双垂泪。 一张机,夏荷残影雨靡靡,流光渐去添新句。清灯小字...
    离之_905d阅读 437评论 0 4
  • yum安装 1.node.js是用c++写的,所以电脑环境首先要有c++的环境; 2.进入你想要安装的目录,下载n...
    chenxuxu阅读 553评论 0 2
  • 既然缘尽于此,不如就此别过
    熊胖之语阅读 127评论 0 0