【JobScheduler】Android 后臺任務調度的核心組件指南

JobScheduler 是 Android 平臺上原生支持在直接啟動模式(Direct Boot Mode)下執行任務的調度器。 相比 WorkManager 需要復雜的配置才能勉強支持直接啟動,JobScheduler 在這方面有著天生的優勢和明確的 API 支持。

在這里插入圖片描述

如果你面臨的硬性要求是必須在用戶解鎖前就執行后臺任務,那么從 WorkManager 切換到 JobScheduler 是一個非常明智且正確的選擇。

JobScheduler 如何支持直接啟動模式?

JobScheduler 通過其構建器 JobInfo.Builder 中的一個關鍵方法來實現:

  • .setPersisted(true): 這個方法用于設置任務在設備重啟后是否依然有效。這是所有重啟后任務的基礎。
  • .setRequiresDeviceIdle(false).setRequiresCharging(false): 在直接啟動模式下,設備通常不被認為是“空閑”的,所以需要放寬這些限制。
  • .setDirectBootAware(true) (API 28+, Android P): 從 Android 9.0 開始,JobInfo.Builder 增加了一個專門的方法,用于明確地將一個任務標記為支持直接啟動
  • 對于 API 24-27 (Android N-O): 雖然沒有 setDirectBootAware 方法,但只要你的應用組件(Service)被標記為 directBootAware="true",并且任務是 persisted 的,系統就會在直接啟動模式下調度它。

如何使用 JobScheduler 實現你的需求

下面是一個完整的示例,展示了如何使用 JobScheduler 來替代 WorkManager,并確保任務能在直接啟動模式下運行。

第 1 步:創建一個 JobService

JobServiceJobScheduler 任務的實際執行者。它是一個特殊的 Service

DeviceInfoUploadJobService.java

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Build;
import android.os.UserManager;
import android.util.Log;// 必須在 AndroidManifest.xml 中注冊這個 Service
public class DeviceInfoUploadJobService extends JobService {private static final String TAG = "UploadJobService";private volatile boolean isJobCancelled = false;@Overridepublic boolean onStartJob(JobParameters params) {Log.d(TAG, "Job started. Job ID: " + params.getJobId());// 任務在主線程上啟動,必須手動開啟一個后臺線程來執行網絡操作new Thread(() -> {doWork(params);}).start();// 返回 true 表示任務正在進行中(在另一個線程上),// 稍后你會手動調用 jobFinished() 來結束它。return true; }private void doWork(JobParameters params) {// 在這里執行你的設備信息獲取和網絡上報邏輯Log.d(TAG, "執行上報任務...");// 你可以在這里檢測是否處于直接啟動模式UserManager userManager = getSystemService(UserManager.class);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !userManager.isUserUnlocked()) {Log.w(TAG, "警告:當前在直接啟動模式下運行!");// 注意:此時只能訪問設備加密存儲 (DES)} else {Log.i(TAG, "當前在正常模式下運行。");// 可以訪問所有常規數據}// --- 模擬網絡請求 ---try {// 假設這里是你的 HTTP 上報代碼Thread.sleep(5000); // 模擬耗時操作if (isJobCancelled) {Log.w(TAG, "Job was cancelled before completion.");return;}Log.d(TAG, "上報成功!");// 任務成功完成后,必須調用 jobFinished// 第二個參數 false 表示不需要重新調度這個任務jobFinished(params, false); // 在這里可以調度下一次 24 小時的任務scheduleNextJob(this);} catch (Exception e) {Log.e(TAG, "上報失敗: ", e);// 任務失敗時,也需要調用 jobFinished// 第二個參數 true 表示希望系統根據退避策略重新調度這個任務jobFinished(params, true);}}// 當系統決定取消正在運行的任務時,這個方法會被調用@Overridepublic boolean onStopJob(JobParameters params) {Log.w(TAG, "Job stopped by system. Job ID: " + params.getJobId());isJobCancelled = true;// 返回 true 表示你希望在條件滿足時重新調度這個任務return true; }// 一個輔助方法,用于調度下一次 24 小時的任務private void scheduleNextJob(Context context) {// ... (見下面的調度器代碼)}
}
第 2 步:在 AndroidManifest.xml 中注冊 JobService

這非常關鍵,并且需要聲明正確的權限和屬性。

<manifest ...><!-- JobService 需要這個權限 --><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:directBootAware="true"  <!-- 你的應用必須支持直接啟動 -->...><serviceandroid:name=".DeviceInfoUploadJobService"android:permission="android.permission.BIND_JOB_SERVICE"android:directBootAware="true" <!-- 關鍵:將 Service 標記為支持直接啟動 -->android:exported="true"/><!-- 你的 BootReceiver 也需要是 directBootAware 的 --><receiverandroid:name=".BootReceiver"android:directBootAware="true"><intent-filter><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver></application>
</manifest>
第 3 步:創建一個任務調度器類

這個類將負責創建和調度 JobInfo

ReportJobScheduler.java

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import java.util.concurrent.TimeUnit;public class ReportJobScheduler {private static final int JOB_ID = 1001;public static void scheduleInitialJob(Context context) {JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);if (jobScheduler == null) return;ComponentName componentName = new ComponentName(context, DeviceInfoUploadJobService.class);JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // 需要網絡連接.setPersisted(true); // 重啟后依然有效// 設置重試策略:30分鐘后,線性退避if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {builder.setBackoffCriteria(TimeUnit.MINUTES.toMillis(30), JobInfo.BACKOFF_POLICY_LINEAR);}// 關鍵:為 Android P 及以上版本明確設置支持直接啟動if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {builder.setDirectBootAware(true);}jobScheduler.schedule(builder.build());}// 你可以在 JobService 成功后調用這個方法來安排下一次public static void scheduleNext24HourJob(Context context) {// ... 類似上面的邏輯,但是可以添加 setMinimumLatency(TimeUnit.HOURS.toMillis(24))}
}
第 4 步:在 BootReceiver 中觸發調度
public class BootReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 無論是在 LOCKED_BOOT_COMPLETED 還是 BOOT_COMPLETED 時,都去調度任務// JobScheduler 會根據網絡狀態來決定何時運行if (intent.getAction() != null) {ReportJobScheduler.scheduleInitialJob(context);}}
}

WorkManager vs JobScheduler (在直接啟動場景下)

特性WorkManagerJobScheduler
Direct Boot 支持間接且復雜。需要手動、有條件地初始化,并管理不同存儲區的上下文。原生支持。通過 setDirectBootAware(true) 明確聲明,系統會自動處理。
API 簡潔性較高。鏈式調用,API 更現代。較低。API 更偏向底層,需要自己管理 JobService 的生命周期和線程。
向后兼容性非常好。在舊版本 Android 上會自動回退到 AlarmManager+BroadcastReceiverJobScheduler僅 API 21+。在舊設備上不可用。
重試/約束非常強大和靈活。提供了基本的網絡、充電、空閑等約束和退避策略。
線程管理自動doWork() 已經在后臺線程上運行。手動onStartJob() 在主線程上,必須自己創建后臺線程。

結論:
對于你的特定問題——必須在直接啟動模式下運行——JobScheduler 是一個技術上更直接、更可靠的選擇。它就是為了這種系統級的、需要在特殊設備狀態下運行的任務而設計的。雖然它需要你手動處理更多的細節(如線程),但它能完美地解決 WorkManager 在這個場景下遇到的初始化和存儲上下文的根本性難題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/95890.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/95890.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/95890.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c# 調用basler 相機

目錄 一聯合halcon&#xff1a; 二 c# 原生 一聯合halcon&#xff1a; 環境配置 下載安裝pylon軟件 下載安裝halcon 創建 winform項目 test_basler 添加引用 打開pylon可以連接相機 可以看到我的相機id為23970642 &#xff08; c#聯合halcon的基礎教程&#xff08;案例…

《2025年AI產業發展十大趨勢報告》四十六

《2025年AI產業發展十大趨勢報告》四十六隨著科技的迅猛發展&#xff0c;人工智能&#xff08;AI&#xff09;作為引領新一輪科技革命和產業變革的戰略性技術&#xff0c;正逐步滲透到各個行業和領域&#xff0c;成為推動經濟社會發展的重要引擎。2023年&#xff0c;生成式AI的…

c++ 雜記

1. 為什么返回*this?2. 3. 友元函數的使用&#xff1a;需要頭文件中類內外聲明&#xff0c;cpp文件中實現定義哦// Sales_data.h #ifndef SALES_DATA_H #define SALES_DATA_H#include <string>class Sales_data {std::string bookNo;int units_sold 0;double revenue …

PDF文件基礎-計算機字體

計算機字體的原理包含了字符編碼、字形渲染和字體文件存儲三個關鍵技術。 字符編碼負責將每個字符映射到一個唯一的數字碼&#xff1b;字形渲染則將這些數字碼轉換成屏幕或紙張上可識別的圖形&#xff1b;字體文件存儲則包含了字符的編碼、圖形描述信息以及字體的其他屬性&…

華為IP(9)

OSPF的基本配置OSPF路由計算前言&#xff1a;1)同一區域內的OSPF路由器擁有完全一致的LSDB&#xff0c;在區域內部&#xff0c;OSPF采用SPF算法完成路由計算。2&#xff09;隨著網絡規模不斷擴大&#xff0c;路由器為了完成路由計算所消耗的內存、CPU資源也越來越多。通過區域劃…

java.nio.file.InvalidPathException異常

一.問題概述 本人在ubuntu22.04的操作系統上&#xff0c;運行java程序時創建一個文件時&#xff0c;由于文件名稱中包含了中文&#xff0c;所以導致了程序拋出了java.nio.file.InvalidPathException的異常。 java.nio.file.InvalidPathException: Malformed input or input co…

Next系統總結學習(一)

下面我按題號逐條 詳細 解釋并給出示例與最佳實踐。為便于閱讀&#xff0c;我會同時給出關鍵代碼片段&#xff08;偽代碼/實用例子&#xff09;&#xff0c;并指出常見坑與解決方案。 1. 你是如何理解服務端渲染&#xff08;SSR&#xff09;的&#xff1f;它的核心工作流程是怎…

房屋安全鑒定需要什么條件

房屋安全鑒定需要什么條件&#xff1a;專業流程與必備要素解析房屋安全鑒定是保障建筑使用安全的重要環節&#xff0c;它通過對建筑結構、材料性能及使用狀況的全面評估&#xff0c;為房屋的安全使用、改造或維護提供科學依據。隨著城市建筑老化及自然災害頻發&#xff0c;房屋…

現代C++:現代C++?

C語言正在走向完美&#xff0c;所以&#xff0c;C語言值得學習&#xff08;甚至研究&#xff09;&#xff0c;這些知識可以成為一切編程的基礎。然而在實踐中&#xff0c;不必全面的使用C語言的各種特性&#xff0c;而應根據工程項目的實際情況&#xff0c;適當取舍&#xff08…

【C++】哈希表實現

1. 哈希概念 哈希(hash)又稱散列&#xff0c;是?種組織數據的方式。從譯名來看&#xff0c;有散亂排列的意思。本質就是通過哈希 函數把關鍵字Key跟存儲位置建立一個映射關系&#xff0c;查找時通過這個哈希函數計算出Key存儲的位置&#xff0c;進行快速查找 1.1 直接定址法…

ai 玩游戲 llm玩街霸 大模型玩街霸 (3)

1. 開源代碼地址&#xff1a; https://github.com/OpenGenerativeAI/llm-colosseum 2. 架構&#xff1a; 3. 圖片&#xff1a; 4. 感覺還是下面的步驟&#xff1a; a. 實時理解游戲當前環境&#xff0c;英雄角色&#xff0c;英雄狀態 b. 根據當前狀態感知&#xff0c;生成英雄…

2025年滲透測試面試題總結-59(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 一、SQL注入全解 二、XSS與文件漏洞 三、服務端漏洞專題 四、職業經驗與能力評估 1、注入攻擊原理是什么…

GPT系列--類GPT2源碼剖析

無需多言&#xff0c;大家應該都用過了&#xff0c;如今都更新到GPT-5了。1. GPT-1回到2018年的NLP&#xff0c;神仙打架&#xff0c;BERT與GPT不分先后。GPT是“Generative Pre-Training”的簡稱&#xff0c;生成式的預訓練。BERT和GPT肯定是GPT難訓練&#xff0c;引用量也是B…

這是一款沒有任何限制的免費遠程手機控制手機的軟件

這是一款沒有任何限制的免費遠程手機控制手機的軟件支持安卓和蘋果1.安裝1.1被控制端安裝airdroid1.2控制端air mirror2.登錄賬號控制端和被控制端登錄同一個賬號3.控制打開控制端軟件選擇要控制的機器直接點“遠程控制“

Observability:更智能的告警來了:更快的分診、更清晰的分組和可操作的指導

作者&#xff1a;來自 Elastic Drew Post 探索 Elastic Stack 告警的最新增強功能&#xff0c;包括改進的相關告警分組、將儀表盤鏈接到告警規則&#xff0c;以及將調查指南嵌入到告警中。 在 9.1 版本中&#xff0c;我們對告警進行了重大升級&#xff0c;幫助 SRE 和運維人員更…

數智之光燃盛景 共同富裕創豐饒

8月29日&#xff0c;2025數博會“一帶一路”國際大數據產業發展暨數智賦能新時代、共同富裕向未來的會議在貴陽國際生態會議中心隆重舉行。作為全球大數據領域的重要盛會&#xff0c;此次活動吸引了來自聯合國機構、國際組織、科研院所、知名企業等社會各界的百余位代表&#x…

【網絡編程】recv函數的本質是什么?

一、為什么說recv函數的本質是 “copy”&#xff1f; recv是用于從網絡連接&#xff08;或其他 IO 對象&#xff09;接收數據的函數&#xff0c;它的核心動作不是 “從網絡上拉取數據”&#xff0c;而是 “把已經到達內核緩沖區的數據復制到用戶程序的緩沖區”。 具體流程拆解&…

JSP程序設計之輸入/輸出對象 — out對象

目錄1、out對象概述2.實例&#xff1a;out對象方法運用輸入/輸出對象&#xff0c;可以控制頁面的輸入和輸出&#xff0c;用于訪問與所有請求和響應有關的數據&#xff0c;包括out、request和response對象。 1、out對象概述 out對象是JspWriter類的一個實例&#xff0c;是一個…

UE里為什么要有提升變量

1、為了簡潔當一個類里面的函數比較多&#xff0c;并且使用比較頻繁的時候&#xff0c;就要不斷的從這個類節點往外拉線&#xff0c;從而獲取不同的函數節點&#xff0c;這樣的藍圖就會看起來比較亂&#xff0c;這時候&#xff0c;就可以將這個常用的類提升為變量。2、為了存儲…

玩轉物聯網只需十行代碼,可它為何悄悄停止維護

文章目錄玩轉物聯網只需十行代碼&#xff0c;可它為何悄悄停止維護1 背景&#xff1a;MQTT 遇上 asyncio&#xff0c;為什么選 hbmqtt&#xff1f;2 hbmqtt 是什么&#xff1f;3 安裝&#xff1a;一行命令&#xff0c;但別裝最新4 五大核心 API&#xff1a;10 行代碼跑通發布訂…