【WorkManager】無法在 Direct Boot 模式下初始化

【WorkManager】無法在 Direct Boot 模式下初始化

  • 一、問題描述
  • 二、問題分析
    • 2.1 關于 Direct Boot 模式
    • 2.2 支持 Direct Boot 模式
    • 2.3 手動初始化 WorkManager 組件
    • 2.4 WorkManager 不支持 Direct Boot 的官方修改
  • 三、解決方案

一、問題描述

在使用 WorkManager 庫來實現開機上報設備信息和定時上報設備信息時,當 Android 設備設置屏幕鎖定密碼并未解鎖時,此時 WorkManager 會無法初始化導致異常無法執行任務。

09-08 09:44:41.744  1827  1827 E AndroidRuntime: FATAL EXCEPTION: main
09-08 09:44:41.744  1827  1827 E AndroidRuntime: Process: com.xxx.devicereport, PID: 1827
09-08 09:44:41.744  1827  1827 E AndroidRuntime: java.lang.RuntimeException: Unable to get provider androidx.startup.InitializationProvider: androidx.startup.StartupException: java.lang.IllegalStateException: Cannot initialize WorkManager in direct boot mode
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.installProvider(ActivityThread.java:7940)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.installContentProviders(ActivityThread.java:7444)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7148)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2328)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.os.Looper.loopOnce(Looper.java:205)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:294)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:8408)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:640)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:982)
09-08 09:44:41.744  1827  1827 E AndroidRuntime: Caused by: androidx.startup.StartupException: java.lang.IllegalStateException: Cannot initialize WorkManager in direct boot mode
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.startup.AppInitializer.doInitialize(AppInitializer.java:187)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:239)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:207)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.startup.InitializationProvider.onCreate(InitializationProvider.java:49)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.content.ContentProvider.attachInfo(ContentProvider.java:2621)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.content.ContentProvider.attachInfo(ContentProvider.java:2590)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at android.app.ActivityThread.installProvider(ActivityThread.java:7935)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        ... 11 more
09-08 09:44:41.744  1827  1827 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Cannot initialize WorkManager in direct boot mode
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.impl.WorkManagerImpl.<init>(WorkManagerImpl.java:237)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.impl.WorkManagerImplExtKt.createWorkManager(WorkManagerImplExt.kt:48)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.impl.WorkManagerImplExtKt.createWorkManager$default(WorkManagerImplExt.kt:29)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.impl.WorkManagerImplExtKt.createWorkManager(WorkManagerImplExt.kt:0)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.impl.WorkManagerImpl.initialize(WorkManagerImpl.java:206)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.WorkManager.initialize(WorkManager.java:212)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.WorkManagerInitializer.create(WorkManagerInitializer.java:39)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.work.WorkManagerInitializer.create(WorkManagerInitializer.java:30)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        at androidx.startup.AppInitializer.doInitialize(AppInitializer.java:180)
09-08 09:44:41.744  1827  1827 E AndroidRuntime:        ... 17 more

二、問題分析

2.1 關于 Direct Boot 模式

Android中的 Direct Boot 模式(直接啟動模式)是Android 7.它允許設備在啟動后、用戶解鎖前的鎖定狀態下運行特定的應用組件,確保關鍵功能(如鬧鐘、緊急通知和無障礙服務)在設備重啟后仍能正常工作。

核心設計目標

Direct Boot模式解決了全盤加密(FDE)設備重啟后需用戶解鎖才能運行應用的問題。通過文件級加密(FBE)和兩個獨立的存儲分區,系統在未解鎖狀態下仍能有限運行:

  • 設備加密存儲(DE):使用設備硬件綁定的密鑰加密,在Direct Boot模式和用戶解鎖后均可訪問。
  • 憑據加密存儲(CE):使用用戶憑據(如密碼、PIN)加密,僅在用戶解鎖后可用。

加密路徑
/data/user_de/0/com.xxx.aidevicereport
非加密
/data/user/0/com.xxx.devicereport

實現方式

組件標記 在AndroidManifest.xml中將組件(Application、Activity、Service、BroadcastReceiver等)設置為android:directBootAware="true"

2.2 支持 Direct Boot 模式

關于設備開啟重啟后的幾個廣播

// 無論直接啟動支持如何,在啟動時立即發送
public static final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";// 目標用戶解鎖憑據加密的專用存儲時發送
public static final String ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED";// 設備解鎖后發送
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";

查詢用戶是否已解鎖設備的方法

UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
if (userManager != null) {boolean isUserUnlocked = userManager.isUserUnlocked();Log.d(TAG, "Application isUserUnlocked:" + isUserUnlocked);
}

訪問設備加密存儲空間的方法,通過此上下文 createDeviceProtectedStorageContext() 發出的所有存儲 API 調用均訪問設備加密存儲空間,包括文件數據庫SharedPreferences

Context directBootContext = appContext.createDeviceProtectedStorageContext();
// Access appDataFilename that lives in device encrypted storage
FileInputStream inStream = directBootContext.openFileInput(appDataFilename);
// Use inStream to read content...

官方文檔:https://developer.android.com/privacy-and-security/direct-boot?hl=zh-cn

2.3 手動初始化 WorkManager 組件

網上很多解決方案是手動完成 WorkManager 組件的初始化,對于大多數的應用是在設備解鎖后使用的是可以解決的,但是在需要在設備未解鎖的狀態下仍需要執行任務的情況就不適用了。

停用 WorkManager 組件自動初始化

 <providerandroid:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"tools:node="remove"></provider>

讓你的 Application 類實現 Configuration.Provider接口,并提供您自己的 Configuration.Provider.getWorkManagerConfiguration。 當您需要使用 WorkManager 時,請務必調用 WorkManager.getInstance(Context)。 WorkManager 會調用應用的自定義 getWorkManagerConfiguration() 方法來發現其 Configuration。(無需自行調用 WorkManager.initialize。)

class MyApplication extends Application implements Configuration.Provider {@Overridepublic Configuration getWorkManagerConfiguration() {return new Configuration.Builder().setMinimumLoggingLevel(android.util.Log.INFO).build();}
}

官方文檔:
https://developer.android.com/topic/libraries/app-startup?hl=zh-cn#manual
https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration?hl=zh-cn

2.4 WorkManager 不支持 Direct Boot 的官方修改

Gerrit Commit:https://android-review.googlesource.com/c/platform/frameworks/support/+/1196948

Throw an exception when trying to initialize WorkManager in direct boot mode.* WorkManager does not support direct boot mode. Therefore when an app tries toinitialize WorkManager in direct boot mode, we now throw an IllegalStateException.Test: Added unit tests.
Change-Id: I549cca9aae0e8d5136a59719226c647dd3b1bb8b
/*** Initializes an instance of {@link WorkManagerImpl}.** @param context The application {@link Context}* @param configuration The {@link Configuration} configuration* @param workDatabase The {@link WorkDatabase} instance* @param schedulers The {@link List} of {@link Scheduler}s to use* @param processor The {@link Processor} instance*/
private void internalInit(@NonNull Context context,@NonNull Configuration configuration,@NonNull TaskExecutor workTaskExecutor,@NonNull WorkDatabase workDatabase,@NonNull List<Scheduler> schedulers,@NonNull Processor processor) {context = context.getApplicationContext();mContext = context;mConfiguration = configuration;mWorkTaskExecutor = workTaskExecutor;mWorkDatabase = workDatabase;mSchedulers = schedulers;mProcessor = processor;mPreferenceUtils = new PreferenceUtils(workDatabase);mForceStopRunnableCompleted = false;// Check for direct boot modeif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && context.isDeviceProtectedStorage()) {throw new IllegalStateException("Cannot initialize WorkManager in direct boot mode");}// Checks for app force stops.mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}

三、解決方案

當應用會在 Direct Boot 模式下啟動并完成初始化,還需要執行相關的任務,這里需要解決 2 個問題,其一是 WorkManager 的初始化,其二是訪問設備加密存儲,操作文件、數據庫或 SharedPreferences,否則可能會出現如下報錯:

09-05 16:17:43.361  1976  2111 E AndroidRuntime: Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: Cannot open database '/data/user/0/com.xxx.devicereport/no_backup/androidx.work.workdb' with flags 0x30000000: Directory /data/user/0/com.revomovil.devicereport/no_backup doesn't exist
09-04 14:47:40.445  5355  5355 E AndroidRuntime: Caused by: java.lang.IllegalStateException: SharedPreferences in credent
ial encrypted storage are not available until after user (id 0) is unlocked

因為應用是支持 Direct Boot 模式是,所以你的應用需要聲明 directBootAware

<applicationandroid:name=".DeviceReportApplication"android:directBootAware="true"android:label="@string/app_name"android:persistent="true"android:usesCleartextTraffic="true" />

規避 WorkManager 的初始化時的 IllegalStateException: Cannot initialize WorkManager in direct boot mode,根據源碼處的判斷將 isDeviceProtectedStorage() 返回 false 即可

public class DeviceReportApplication extends Application {private static final String TAG = "DeviceReportApplication";/*** default false to androidx.work.*/@Overridepublic boolean isDeviceProtectedStorage() {return false;}
}

因為 WokrManager 內部實現的數據庫訪問無法修改,所以需要全局訪問設備加密存儲,有 2 種方式。

可以直接在 Application 標簽下聲明 defaultToDeviceProtectedStorage

<applicationandroid:name=".DeviceReportApplication"android:directBootAware="true"android:defaultToDeviceProtectedStorage="true"android:label="@string/app_name"android:persistent="true"android:usesCleartextTraffic="true" />

或者在 Application 中替換全局 Context,修改 attachBaseContext

public class DeviceReportApplication extends Application {private static final String TAG = "DeviceReportApplication";@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base.createDeviceProtectedStorageContext());}/*** default false to androidx.work.*/@Overridepublic boolean isDeviceProtectedStorage() {return false;}
}

在這里插入圖片描述

相關參考

深入了解 Jetpack WorkManager: 高效的后臺任務調度

Android DirectBoot模式及其數據存儲

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

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

相關文章

Hybrid應用性能優化實戰分享(本文iOS 與 H5為例,安卓同理)

前言在移動應用開發中&#xff0c;Hybrid 架構因其跨平臺特性和開發效率優勢被廣泛采用。然而&#xff0c;WebView 的性能問題一直是開發者面臨的挑戰。本文將基于實際項目經驗&#xff0c;分享 iOS Hybrid 應用的核心優化策略&#xff0c;涵蓋 WebView 池化、預加載、用戶體驗…

點積、叉積、矩陣行列式詳解、線性相關與線性無關、矩陣的秩、矩陣可逆與不可逆詳解

1.向量 1.1 點積&#xff08;Dot Product&#xff09; 1.1.1 定義 點積是在求一個標量&#xff0c;點積結果沒有方向。 對于兩個向量u(u1,u2,u3),v(v1,v2,v3)\bold{u}(u_1,u_2,u_3),\bold{v}(v_1,v_2,v_3)u(u1?,u2?,u3?),v(v1?,v2?,v3?) 點積定義為&#xff1a;u?vu1v1u…

Mac安裝nvm詳細教程(超簡單)

本章教程,主要介紹如何在Mac操作系統上安裝nvm. 我們使用官方一鍵安裝腳本,完成安裝 一、安裝步驟 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash配置環境變量,編輯.zshrc文件 vim .zshrcexport NVM_DIR="$(

【selenium】網頁元素找不到?從$(‘[placeholder=“手機號“]‘)說起

網頁元素找不到&#xff1f;從$(‘[placeholder“手機號”]’)說起總結&#xff1a;控制臺不騙人&#xff0c;元素選不到&#xff0c;八成是寫法、時機或環境的問題。我們在寫網頁自動化腳本或者調試頁面的時候&#xff0c;經常遇到一個讓人頭疼的問題&#xff1a;明明元素就在…

SSE 模仿 GPT 響應

后端代碼 const express require(express) const cors require(cors);const app express(); app.use(cors()); const port 3000;app.listen(port, () > {console.log(Server running at http://localhost:${port}/); });const msg 全國同胞們&#xff0c; 尊敬的各位國…

MAC 多個版本 JDK進行切換

1.查看本機所有的jdk/usr/libexec/java_home -V2、打開bash_profile文件。可以在終端vim ~/.bash_profile打開&#xff0c;也可以打開訪達shiftcmdG然后輸入/Users/mac/.bash_profile&#xff08;本機bash_profile的路徑&#xff09;加入新的環境變量格式如下&#xff08;參考我…

shell 中 expect 詳解

一、概述Expect是一個免費的編程工具語言&#xff0c;用來實現自動和交互式任務進行通信&#xff0c;而無需人的干預。Expect的作者DonLibes在1990年開始編寫Expect時對Expect做有如下定義&#xff1a;Expect是一個用來實現自動交互功能的軟件套件。通過expect系統管理員可以創…

第4講 機器學習基礎概念

機器學習作為人工智能的子領域&#xff0c;專注于訓練計算機算法自動發現數據中的模式與關聯關系。以下是其核心基礎概念&#xff1a;4.1 數據數據是機器學習的基石。缺乏數據&#xff0c;算法將無從學習。數據可呈現為結構化數據&#xff08;如電子表格、數據庫&#xff09;和…

Go組合式繼承:靈活替代方案

Go 語言沒有傳統面向對象編程中的繼承機制&#xff0c;但通過組合和接口實現類似功能。Go 更提倡組合優于繼承的設計原則&#xff0c;這種設計方式更靈活且易于維護。結構體組合&#xff08;偽繼承&#xff09;通過嵌套結構體實現類似繼承的效果。子結構體可以直接訪問父結構體…

Verilog三段式FSM,實現十字路口紅綠燈

運行環境&#xff1a;VCS verdi狀態說明&#xff1a;S0 &#xff1a; 初始狀態 S1 &#xff1a; 東西方向綠燈亮&#xff0c;南北方向紅燈亮&#xff1b;點亮30周期 S2 &#xff1a; 東西方向黃燈亮&#xff0c;南北方向紅燈亮&#xff1b;點亮2 周期 S3 &#xff1a; 東西方向…

java 將pdf轉圖片

如何將pdf文件轉為圖片 import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class Pdf2Png {/**…

手搓Spring

目錄 兩種方法創建Spring容器 自定義Spring容器及前置操作 Spring掃描邏輯實現 createBean()方法 getBean()方法 依賴注入&#xff08;DI&#xff09; BeanNameAware接口 InitializingBean接口 BeanPostProcessor接口 AOP的實現 Spring 是一個輕量級的 Java 開發框架…

.NET 單文件程序詳解:從原理到實踐

C# 混淆加密大師在最新版本中, 提供了.NET單文件解包打包功能, 它可以快速解包官方打包的單文件程序&#xff0c;恢復為原始的多文件結構。也可以對解包后的程序集進行混淆與加密&#xff0c;有效提升逆向門檻。最后還能重新打包成單文件程序&#xff0c;保持對用戶友好的分發形…

Spring面試題記錄?

請簡述 Spring 框架的核心是什么&#xff1f;它主要包含了哪些核心模塊&#xff1f; spring的核心模塊主要有spring-core&#xff08;工具類&#xff0c;資源加載&#xff09;&#xff0c;spring-bean&#xff08;bean的定義&#xff0c;創建&#xff0c;封裝&#xff09;&…

一次緩存引發的文件系統數據不一致問題排查與深度解析

01 起因EFC&#xff08;Elastic File Client&#xff09;是 NAS 自研的分布式文件系統客戶端&#xff0c;最近完成了對緩存架構的更新&#xff0c;現在支持多個客戶端之間構成分布式緩存&#xff0c;底層支持 NAS、CPFS 和 OSS。由于開發時間較短&#xff0c;一直沒有做 NAS 場…

Spring Boot Gateway 教程:從入門到精通

一、Spring Cloud Gateway 簡介Spring Cloud Gateway 是基于 Spring 5、Project Reactor 和 Spring Boot 2 構建的 API 網關&#xff0c;旨在為微服務架構提供一種簡單而有效的路由管理方式。它取代了 Netflix Zuul&#xff0c;提供了更高效和更強大的網關解決方案。核心特點&a…

防火墻 只允許信任的幾臺服務器訪問

1. 首先&#xff0c;確保 firewalld 服務正在運行&#xff1a;systemctl start firewalld systemctl enable firewall2. 設置默認拒絕規則&#xff1a;設置默認拒絕所有流量&#xff08;拒絕所有的入站流量&#xff09;&#xff1a;firewall-cmd --zonepublic --add-rejectal…

十三,數據結構-樹

定義樹也是基于節點的數據結構&#xff0c;和鏈表不同的是&#xff0c;樹的節點可以指向多個節點。首先對樹的一些常用術語進行說明&#xff1a;最上面的節點叫做根節點&#xff0c;根位于樹頂&#xff0c;如圖中的節點A&#xff1b;和族譜一樣&#xff0c;節點有后代和祖先&am…

JVM-默背版

1.JVM對sychronized的優化&#xff1a;鎖膨脹、鎖消除、鎖粗化、自適應自旋鎖 &#xff08;1&#xff09;鎖膨脹&#xff1a;從無鎖、偏向鎖、輕量級鎖、重量級鎖的過程叫做鎖膨脹。在JDK1.6以前&#xff0c;sychronized是由重量級鎖實現的&#xff0c;加鎖和解鎖的過程需要從用…

Mac M 系列芯片 YOLOv8 部署教程(CPU/Metal 后端一鍵安裝)

在 Mac M 系列芯片&#xff08;Apple Silicon/ARM 架構&#xff09;上部署 YOLOv8&#xff0c;有一些注意事項&#xff1a;PyTorch 需要安裝 ARM 原生版本&#xff0c;推理可利用 Metal 后端加速 CPU。本文教你一步步完成環境配置、模型下載、依賴安裝和驗證推理。1?? 環境準…