android workmanager

目錄

  • google后臺(tái)任務(wù)推薦方案
  • doze簡介
  • 從jobservice 到android-job 到workmanager
  • 遇到的一些坑
  • 總結(jié)

先看一下google推薦的后臺(tái)任務(wù)的解決方案

image.png

如圖后臺(tái)任務(wù)可以從三個(gè)維度去考慮:

  • 是否需要立即執(zhí)行
  • 是否需要執(zhí)行完畢
  • 是否需要監(jiān)聽系統(tǒng)的狀態(tài) 如網(wǎng)絡(luò)狀態(tài)等

然后給出了對(duì)應(yīng)的具體的推薦方案

線程池、廣播、前臺(tái)任務(wù)這里暫時(shí)不提,主要看一下新出的workmanager,從圖中可以看出workmanager的使用條件任務(wù)可以適當(dāng)延遲的,即task沒有鬧鐘那種強(qiáng)時(shí)效性。

workmanager是今年google io提出來的,目前版本還是1.0.0-alpha01,看見alpha就有點(diǎn)慌,后面果然出現(xiàn)bug了,這個(gè)后面再細(xì)說。

背景

為什么要使用workmanager,是alrammanager不好用嗎?
其實(shí)這兩者的作用并不完全一樣;alarmmanager適用于類似鬧鐘那樣必須準(zhǔn)時(shí)喚醒的任務(wù),不管是否處于doze(低耗電和應(yīng)用待機(jī)模式);不過在6.0后的doze里面,alarmmanager需要調(diào)用setAndAllowWhileIdle()
setExactAndAllowWhileIdle()
如果必須要準(zhǔn)時(shí)執(zhí)行的任務(wù)比如股票每日開市提醒只能強(qiáng)行喚醒系統(tǒng),但是也有的是不是要求時(shí)間這么準(zhǔn)確的,比如每隔2天彈一個(gè)notification提示打開app啥的,這種實(shí)效性不那么高的就可以換成jobservice。

簡單介紹一下doze

相關(guān)文檔

image.png

這個(gè)是android6.0后出來的,6.0+的設(shè)備上都有這個(gè),目的是為了省電。簡單的說就是手機(jī)系統(tǒng)發(fā)現(xiàn)一段時(shí)間用戶都沒有在用手機(jī)了,就會(huì)進(jìn)入省電模式,期間不會(huì)處理alarm、網(wǎng)絡(luò)狀態(tài)等,等省電了一段時(shí)間后再給一小段時(shí)間讓所有應(yīng)用統(tǒng)一處理alram等task,然后又進(jìn)入省電狀態(tài)。如果中途一直沒有亮屏、充電等表示用戶開始用手機(jī)了的操作,doze的省電周期會(huì)越來越長,如上圖。
老實(shí)說,按照國人的習(xí)慣,白天手機(jī)想進(jìn)入doze,基本上上不可能的。沒有誰會(huì)好幾個(gè)小時(shí)不碰手機(jī)吧。所以基本上都是晚上進(jìn)入doze,這也說明了為啥早上亮屏的瞬間手機(jī)好多app會(huì)在同一時(shí)間彈出notification(亮屏就退出了doze,被延遲的alram馬上開始工作)。

那么使用jobservice的優(yōu)勢在哪兒,我為什么不全部用alarmManager?

使用jobservice的優(yōu)勢就是doze下,不會(huì)喚醒系統(tǒng),耗電量會(huì)減小。耗電量減小到一定程度,才能達(dá)到googleplay的推薦要求;在國內(nèi),兩種類型差不多的app,用戶通過電量消耗統(tǒng)計(jì)軟件發(fā)現(xiàn)耗電量低的那個(gè)app,應(yīng)該會(huì)對(duì)它更有好感吧(卸載也是先卸載競爭對(duì)手的app 哈哈)。

從jobservice 到android-job 到workmanager

前面說的時(shí)效性不是很強(qiáng)的任務(wù)可以用jobservice(JobScheduler),但是這個(gè)是android5.0+才出來的,要兼容5.0之前的,額外再用alramager;而且國外的還可以使用googleservice(Firebase JobDispatcher)實(shí)現(xiàn)。這就需要開發(fā)者自己來兼容;
后面發(fā)現(xiàn)印象筆記的android-job已經(jīng)實(shí)現(xiàn)了類似的功能;
github上印象筆記的文檔

androidjob的相關(guān)api

任務(wù)調(diào)度類型

  • 普通任務(wù)
    setExecutionWindow(start,end)
  • 特定時(shí)間執(zhí)行的任務(wù)
    setExact(time)
  • 周期任務(wù)
    setPeriodic(long intervalMs, long flexMs)

任務(wù)的執(zhí)行時(shí)間范圍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.
    這兩個(gè)是時(shí)間窗口的起始時(shí)間,意思是System.currentTimeMillis() + startInMs到System.currentTimeMillis() + endInMs之間執(zhí)行,endInMs對(duì)應(yīng)后面的deadline。
    在setPeriodic(long)和setExact(long)時(shí)不起作用

和周期任務(wù)相關(guān)的參數(shù)Builder setPeriodic(long intervalMs, long flexMs)

  • long mIntervalMs
    這個(gè)是周期執(zhí)行有關(guān)的參數(shù),每隔多久執(zhí)行一次,最低是15分鐘。

  • long mFlexMs
    這個(gè)是周期執(zhí)行有關(guān)的參數(shù),離周期末尾多久這個(gè)job應(yīng)該被執(zhí)行,最低5分鐘。

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

    debug模式下設(shè)置這個(gè),可以在api<24的機(jī)子上周期減小到60s,倒數(shù)減小到30s;正式環(huán)境,最低周期15分鐘,倒數(shù)5分鐘。

和任務(wù)執(zhí)行失敗,重新調(diào)度有關(guān)的參數(shù)public Builder setBackoffCriteria(long backoffMs, @NonNull BackoffPolicy backoffPolicy)

  • backoffMs
    非周期性的任務(wù),執(zhí)行失敗后被重新調(diào)度需要等待的時(shí)間,配合著下面的策略使用。
    默認(rèn)開始30s,逐漸增加最多5小時(shí)
  • backoffPolicy
    枚舉,只有線性和指數(shù)增長兩種方式。

一些二級(jí)條件吧,如果沒有強(qiáng)制要求,會(huì)在deadline時(shí)忽略這些條件執(zhí)行job

  • boolean mRequiresCharging
    是否需要插入設(shè)備(是指充電吧)
    默認(rèn)false,但是如果之前的endInMS到了&&mRequirementsEnforced=false,就會(huì)忽略這個(gè)條件執(zhí)行job

  • boolean mRequiresDeviceIdle
    是否需要等到設(shè)備空閑。同上面一樣,當(dāng)deadline到來&&沒有mRequirementsEnforced=false時(shí),就會(huì)忽略這個(gè)條件執(zhí)行Job

  • boolean mRequiresBatteryNotLow
    是否禁止低電量。同上,deadline&&有mRequirementsEnforced=false會(huì)忽略。

  • boolean mRequiresStorageNotLow
    是否禁止低存儲(chǔ)空間。這個(gè)只有在android o才有效果

  • NetworkType mNetworkType
    需要等待的網(wǎng)絡(luò)狀態(tài),是netWorkType的枚舉,有好幾種,類似wify 、流量等,默認(rèn)是不需要關(guān)心網(wǎng)絡(luò)狀態(tài)。
    同上條件。

  • boolean mRequirementsEnforced
    這個(gè)就是前面提到的那個(gè)參數(shù),如果設(shè)為false,當(dāng)job到deadline還沒執(zhí)行的時(shí)候,就可以搶救一下。

傳參相關(guān)

  • PersistableBundleCompat mExtras
    帶的額外參數(shù),必須是這種類型,類似bundle??梢栽趏nRunJob方法那里通過傳過去的param取出來。

可以說android-job很良心了,暴露的api簡單易用,功能強(qiáng)大。

workmanager的使用

本來已經(jīng)使用android-job了,但是后面在github文檔的后面看到了官方的說明:由于google出了workmanager,Android-job后面可能不再更新。然后又換到workmanager。

developer上workmanager的官方文檔
使用起來還是很方便的,workmanager.enque(workRequest)就可以了,看起來類似google之前出的volley的風(fēng)格,不知道是不是同一批程序員開發(fā)的哈哈。

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

  //可以再這里調(diào)用下一個(gè)一次性任務(wù),形成周期循環(huán)。
 //現(xiàn)在默認(rèn)執(zhí)行成功,有需要再添加retry back-off相關(guān)邏輯
    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();
   //下一次任務(wù)開始時(shí),取消上一次相關(guān)tag類型的任務(wù)(避免網(wǎng)絡(luò)狀態(tài)不太好反復(fù)觸發(fā)網(wǎng)絡(luò)切換廣播的情況)
   //這里如果queue里面存在100個(gè)以上job會(huì)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);
  }
}

需要注意的是隊(duì)列里面任務(wù)(還在等待調(diào)度 未執(zhí)行的那種)不能超過100個(gè),不然會(huì)crash,這是workmanager代碼的限制
workRequest子類有oneTime和periodic,對(duì)應(yīng)一次性任務(wù)和周期性任務(wù);因?yàn)榛旧贤瑯邮菍?duì)jobservice操作,所以方法很類似android-job,可以對(duì)照上面anroid-job用法或者查看developer文檔workmanger簡介
列一下常見用法

  • 多任務(wù)調(diào)度順序
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();
  • 設(shè)置任務(wù)執(zhí)行的約束條件
// 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();
  • 取消任務(wù)除了前面提到的cancleByTag,也可以用uniquework
 OneTimeWorkRequest oneTimeWorkRequest =
          new OneTimeWorkRequest.Builder(AlarmReceiverWorker.class).build();
      WorkManager.getInstance()
          .beginUniqueWork("downloadQueue", ExistingWorkPolicy.REPLACE, oneTimeWorkRequest);

說說坑吧

workermanager的周期任務(wù),有時(shí)不能取消掉之前放到隊(duì)列的任務(wù)。通過cancleByTag也不行,這個(gè)在stackoverflow和github上面的issue也看到有人提過,而且確實(shí)回復(fù)是個(gè)bug,應(yīng)該是alpha版還是有些問題的。
既然周期任務(wù)有坑,我們也可以采用一次性任務(wù)開啟下一個(gè)一次性任務(wù),像鏈表那樣。而且可以避開周期任務(wù)里面源碼對(duì)周期設(shè)置最小15分鐘的限制,不過一般也沒那么流氓要隔幾分鐘就喚醒吧。
但是使用一次性任務(wù)循環(huán)觸發(fā),發(fā)現(xiàn)在小米上測試,打開app退到后臺(tái)時(shí),alarmmanager android-job workmanager正常,殺掉app,workmanager就不行了;使小米達(dá)到doze的條件,再亮屏,anroid-job也不行了。在小米上暫時(shí)只有alramanager適用(如果有哪位朋友發(fā)現(xiàn)小米上用workmanager有方法可以避開這個(gè)坑,請分享一下)。

還有就是前面提到的,要避開queue里面出現(xiàn)100個(gè)待調(diào)度的job的case。

總結(jié)

最后的做法定時(shí)任務(wù)使用alrammanager,等收到alram廣播后交給worker處理;網(wǎng)絡(luò)狀態(tài)監(jiān)聽任務(wù)直接再接收到廣播后交給worker處理。保證定時(shí),但是收到廣播放到隊(duì)列里面不像之前那樣接收到廣播就串行馬上執(zhí)行了,等系統(tǒng)決定統(tǒng)一處理。

等后面workmanager版本號(hào)把a(bǔ)lpha去掉就更好了;可以先用android-job,等workamanager出穩(wěn)定版本再替換成worker,因?yàn)閍pi太像了,替換的成本也不大。

這篇文章有些細(xì)節(jié)需要后面再進(jìn)行補(bǔ)充。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,094評(píng)論 25 709
  • 1. WorkManager 1). 簡介 其實(shí)就是"管理一些要在后臺(tái)工作的任務(wù), -- 即使你的應(yīng)用沒啟動(dòng)也能...
    _凌浩雨閱讀 718評(píng)論 0 1
  • 一寸柔腸一縷絲,千梭萬紉九張機(jī)。織就鴛鴦?dòng)蓪?duì),明珠勿復(fù)雙垂淚。 一張機(jī),夏荷殘影雨靡靡,流光漸去添新句。清燈小字...
    離之_905d閱讀 497評(píng)論 0 4
  • yum安裝 1.node.js是用c++寫的,所以電腦環(huán)境首先要有c++的環(huán)境; 2.進(jìn)入你想要安裝的目錄,下載n...
    chenxuxu閱讀 617評(píng)論 0 2
  • 既然緣盡于此,不如就此別過
    熊胖之語閱讀 173評(píng)論 0 0

友情鏈接更多精彩內(nèi)容