Android设备加密状态下SharedPreferences访问异常解决方案

Android TV开发踩坑记:SharedPreferences在设备加密时的黑屏问题解决方案 本文深入剖析了Android TV开发中,因设备加密导致应用在直接启动模式下访问SharedPreferences时引发黑屏或闪退的问题。文章从Android系统的直接启动模式与存储分区机制入手,为系统应用和普通应用分别提供了针对性的解决方案,并分享了TV平台特有的实战技巧与排查方法,帮助开发者彻底解决这一棘手难题。 阅读详情

1. 问题来了:为什么我的App一启动就黑屏闪退?

最近在搞一个TV项目,用的是Android 9.0的系统。项目上线前测试,发现一个挺诡异的问题:Launcher(也就是电视的主界面应用)刚启动的时候,偶尔会出现黑屏或者闪一下屏,然后应用就崩溃退出了。这可不是小问题,用户一开机就看到这个,体验直接降到冰点。

赶紧连上Logcat看日志,果然抓到了“罪魁祸首”。错误堆栈里明明白白地写着:

AndroidRuntime: java.lang.RuntimeException: Unable to create application com.example.myapp.MyApplication: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked

翻译一下就是:“无法创建应用,因为抛出了一个非法状态异常:在用户解锁设备之前,存储在凭据加密存储中的SharedPreferences是不可用的。”

看到这个错误,我当时第一反应是有点懵的。SharedPreferences不是用来存一些简单的配置信息,比如用户设置、开关状态什么的吗?它怎么会和“用户解锁”扯上关系?而且我们TV设备很多时候是常开待机的,或者开机就直接进主界面,没有像手机那样的锁屏密码界面,哪来的“解锁”?

后来我花了不少时间查资料、做实验,才把这里面的门道搞清楚。原来,从Android 7.0(Nougat)开始,谷歌引入了一个非常重要的安全特性,叫做“直接启动”(Direct Boot)。这个特性本意是好的,是为了在设备开机后、用户输入密码解锁前,也能让一些关键应用(比如闹钟、来电拦截)有限度地运行。但就是这个特性,给我们这些在“用户解锁”前就需要访问数据的普通应用,挖了一个大坑。

简单来说,Android把存储空间分成了两个区域:

  1. 设备加密存储(Device Encrypted Storage):设备一启动就能访问。这部分存储的密钥和设备硬件绑定,开机就能解密。
  2. 凭据加密存储(Credential Encrypted Storage):必须等用户解锁(输入密码、图案、指纹等)后才能访问。这部分存储的密钥和用户的解锁凭证绑定,安全性更高。

而我们平时默认使用的 Context.getSharedPreferences(),数据就是存在凭据加密存储里的!在用户解锁设备前,你去读它写它,系统就会毫不客气地抛出上面那个 IllegalStateException,导致你的应用崩溃。TV应用在开机启动时,系统可能还处在“直接启动”模式下,用户解锁状态未就绪,所以一碰SharedPreferences就“炸”了。

2. 刨根问底:理解“直接启动”与存储加密

要彻底解决这个问题,不能光知道个解决方案就完事,我们得稍微深入一点,理解Android是怎么管理启动和存储安全的。这样以后遇到类似问题,你才能举一反三。

2.1 什么是“直接启动”模式?

你可以把“直接启动”想象成手机的“安全安全模式”。当你按下手机电源键开机,到出现锁屏界面让你输入密码、指纹的这段时间,手机就运行在“直接启动”模式下。

在这个模式下,系统为了安全,做了严格的限制:

  • 核心系统服务在运行,保证手机能开机。
  • 被显式声明为 directBootAware 的应用可以有限运行。这类应用通常是闹钟、电话、短信这些基础功能应用。
  • 存储访问受限:只能访问“设备加密存储”区域。我们应用默认的数据目录(也就是凭据加密存储)是禁区。

对于TV或者一些物联网设备,它们可能没有设置锁屏密码,但系统启动流程依然会经历这个阶段。如果你的应用(比如Launcher)被设置为开机自启动,就非常有可能撞上这个阶段,从而触发存储访问异常。

2.2 存储加密的“双分区”

前面提到了,Android文件加密(FBE, File-Based Encryption)把数据分成了两个“保险箱”:

存储区域可访问时机密钥来源默认存储位置
设备加密存储 (DE)设备启动后立即可用与设备硬件绑定系统应用、directBootAware应用的部分数据
凭据加密存储 (CE)用户成功解锁设备后与用户解锁凭证(密码等)绑定所有应用默认的数据目录(包括SharedPreferences)
  • /data/data/你的包名/ 这个我们最熟悉的路径,其实是在CE区域里。所以,在直接启动模式下,这个路径下的所有文件,包括通过 getSharedPreferences 创建的XML文件,都是“锁着”的。
  • 系统专门为直接启动模式准备了一个对应的DE存储路径:/data/user_de/0/你的包名/。只有这里的文件,才能在开机未解锁时被访问。

那么问题就变成了:我们能否让SharedPreferences把文件写到DE区域,或者至少在需要的时候,能正确地从DE区域读取数据?

答案是肯定的,而且Android API已经提供了支持。但具体怎么做,取决于你的应用是“系统应用”还是“普通应用”,这两者的处理方式有根本区别。

3. 解决方案一:系统应用的“绿色通道”

如果你的应用是系统应用(比如预装在 /system/app、/system/priv-app 目录下,或者有系统平台签名),那么恭喜你,解决方案非常简单直接。你甚至不需要修改任何代码。

什么是系统应用? 简单判断:你的APK是随着系统镜像一起编译、烧录到设备里的,而不是用户后来从应用市场安装的。很多设备的Launcher、设置等核心应用都属于这一类。

对于系统应用,你只需要在 AndroidManifest.xml 文件的 <application> 标签里,加上两个属性:

<application
    android:directBootAware="true"
    android:defaultToDeviceProtectedStorage="true"
    ... >
    ...
</application>

这两个属性分别是什么意思呢?

  • android:directBootAware="true":告诉系统:“我这个应用知道直接启动这回事,并且能在那个模式下正常工作”。系统会在直接启动阶段就初始化你的应用组件(比如BroadcastReceiver接收开机广播)。
  • android:defaultToDeviceProtectedStorage="true":这是关键! 它告诉系统,我这个应用默认使用的存储上下文(Context)应该是设备加密存储(DE)的上下文。这意味着,从此以后,你在应用里调用 getSharedPreferences()、openFileOutput() 等存储相关API,只要没有指定特殊的Context,它都会自动指向DE存储区域。

实测效果: 我给TV Launcher的Manifest加上这两行后,重新编译系统镜像并烧录。再次开机,黑屏和闪退现象完全消失,Launcher能够正常启动并读取到之前保存的配置。因为现在它读写SharedPreferences时,访问的是随时可用的DE区域,再也不会因为CE区域被锁定而崩溃了。

重要提示: 这个方法仅对系统应用有效。对于普通应用(即用户安装的APP),android:defaultToDeviceProtectedStorage="true” 这个属性是不生效的!系统会直接忽略它。这是出于安全考虑,防止普通应用随意将敏感数据存到安全性较低的DE区域。

4. 解决方案二:普通应用的“精准操作”

我们开发的大多数应用都是普通应用,需要上传到应用市场让用户下载安装。对于这类应用,上面的“绿色通道”走不通了。我们不能默认把所有数据都存到DE区域,因为DE区域的安全性低于CE区域,不适合存放用户隐私数据(比如登录令牌、个人设置等)。

但是,我们可能确实有一些数据需要在开机时就读取,比如:

  • App的夜间模式开关。
  • 一些不影响隐私的UI配置。
  • 记录App是否首次启动的标记。

对于这种需求,Android提供了更精细的控制方式:我们可以创建两个不同的SharedPreferences实例,分别用于存储不同类型的数据。

4.1 核心API:createDeviceProtectedStorageContext()

关键就在于 Context 类里的这个方法:

Context deviceProtectedStorageContext = appContext.createDeviceProtectedStorageContext();

这个方法会返回一个全新的Context对象,这个对象的所有存储操作都会指向设备加密存储(DE)区域。

4.2 实战代码:双存储策略

我们的策略是:将需要在直接启动阶段访问的数据,单独存一份到DE区域。其他敏感或用户解锁后才需要的数据,依然使用默认的CE区域。

第一步:声明应用支持直接启动 即使不是系统应用,如果你想在直接启动阶段做点事情(比如初始化一个不依赖CE存储的组件),也需要在Manifest里声明:

<application
    android:directBootAware="true"
    ... >
    ...
</application>

第二步:在代码中动态选择存储位置 我们需要一个工具类来管理两种SharedPreferences:

public class SafePrefsManager {
    private static final String PREFS_NAME_DEVICE = "myapp_device_prefs";
    private static final String PREFS_NAME_CREDENTIAL = "myapp_credential_prefs";

    /**
     * 获取设备加密存储的SharedPreferences(始终可用)
     * 用于存储开机时必须读取的非敏感配置,如:isFirstLaunch, nightMode, defaultLayoutId等
     */
    public static SharedPreferences getDeviceProtectedPrefs(Context context) {
        Context deContext = context.createDeviceProtectedStorageContext();
        return deContext.getSharedPreferences(PREFS_NAME_DEVICE, Context.MODE_PRIVATE);
    }

    /**
     * 获取默认(凭据加密)存储的SharedPreferences(用户解锁后可用)
     * 用于存储用户敏感数据,如:userId, authToken, personalSettings等
     */
    public static SharedPreferences getCredentialProtectedPrefs(Context context) {
        // 使用原生的context,指向CE区域
        return context.getSharedPreferences(PREFS_NAME_CREDENTIAL, Context.MODE_PRIVATE);
    }

    /**
     * 一个智能获取方法,根据数据键名自动选择存储位置
     * 这需要你事先定义好哪些key存DE,哪些存CE
     */
    public static SharedPreferences getPrefsForKey(Context context, String key) {
        if (isDeviceProtectedKey(key)) {
            return getDeviceProtectedPrefs(context);
        } else {
            return getCredentialProtectedPrefs(context);
        }
    }

    private static boolean isDeviceProtectedKey(String key) {
        // 定义哪些配置需要开机即用
        Set<String> deviceKeys = new HashSet<>(Arrays.asList(
            "is_first_launch",
            "ui_night_mode",
            "default_tab_index"
        ));
        return deviceKeys.contains(key);
    }
}

第三步:在Application初始化中安全地读取 在你的 Application.onCreate() 方法里,你需要非常小心地初始化:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        try {
            // 尝试读取一个必须开机就有的DE配置
            SharedPreferences devicePrefs = SafePrefsManager.getDeviceProtectedPrefs(this);
            boolean isFirstLaunch = devicePrefs.getBoolean("is_first_launch", true);

            if (isFirstLaunch) {
                // 执行首次启动逻辑...
                devicePrefs.edit().putBoolean("is_first_launch", false).apply();
            }

            // 读取其他DE配置,如夜间模式
            int nightMode = devicePrefs.getInt("ui_night_mode", 0);
            applyNightMode(nightMode);

        } catch (IllegalStateException e) {
            // 理论上,读取DE存储不应该抛出此异常。
            // 但如果发生,可能是极端情况,这里做降级处理,比如使用默认值。
            Log.e("MyApp", "Failed to read device prefs, use defaults", e);
        }

        // 注意:此时不要尝试读取 getCredentialProtectedPrefs!
        // 用户解锁后,我们可以再读取CE存储的数据。
    }

    @Override
    public void onUnlocked() {
        // 这是一个可选的回调,当用户解锁设备后调用(API 24+)
        super.onUnlocked();
        // 现在可以安全地读取CE存储的敏感数据了
        SharedPreferences credentialPrefs = SafePrefsManager.getCredentialProtectedPrefs(this);
        String userToken = credentialPrefs.getString("auth_token", null);
        // ... 用敏感数据初始化应用的其他部分
    }
}

4.3 数据迁移的考虑

如果你的应用已经发布,用户本地CE区域已经存有旧数据,现在想将其中一部分非敏感数据迁移到DE区域以供开机使用,该怎么办?

你需要在用户首次解锁设备后(即能访问CE数据时),执行一次迁移操作:

// 在onUnlocked()或主Activity中用户确认已解锁后执行
private void migrateToDeviceStorageIfNeeded(Context context) {
    SharedPreferences oldCredentialPrefs = context.getSharedPreferences("old_prefs", Context.MODE_PRIVATE);
    SharedPreferences newDevicePrefs = SafePrefsManager.getDeviceProtectedPrefs(context);

    // 检查是否已经迁移过
    if (!newDevicePrefs.getBoolean("migration_done", false)) {
        SharedPreferences.Editor editor = newDevicePrefs.edit();
        // 迁移非敏感数据
        editor.putBoolean("ui_night_mode", oldCredentialPrefs.getBoolean("night_mode", false));
        editor.putInt("default_tab_index", oldCredentialPrefs.getInt("default_tab", 0));
        editor.putBoolean("migration_done", true);
        editor.apply();
        Log.i("Migration", "Non-sensitive data migrated to DE storage.");
    }
}

5. 避坑指南与最佳实践

在实际项目中踩过几次坑之后,我总结了一些非常重要的注意事项,能帮你节省大量调试时间。

1. 严格区分数据敏感性 这是最重要的原则。千万不要把任何用户隐私、密码、令牌、个人身份信息(PII)存放到设备加密存储(DE)中。 DE区域在设备开机后即可被任何具有存储权限的应用(在已Root的设备上更危险)潜在访问,安全性远低于CE区域。只将那些真正影响应用启动、且不涉及隐私的配置项放在DE里。

2. 处理好 createDeviceProtectedStorageContext() 的路径问题 这个方法返回的Context,其文件操作路径和默认Context是不同的。这意味着:

  • 你用默认Context存的 shared_prefs/my_prefs.xml 文件。
  • 和你用DE Context存的 shared_prefs/my_prefs.xml 文件。 它们是两个完全不同的文件,存放在物理磁盘的不同位置! 务必在你的代码和文档中清晰区分,避免混淆。

3. 测试,测试,再测试 这个问题在模拟器或已解锁的开发机上很难复现。你必须测试“直接启动”场景:

  • 最有效的方法:在真机上测试。重启设备,在出现锁屏界面(或系统完全启动)前,观察你的应用行为。对于TV,测试冷启动开机流程。
  • 使用adb命令模拟:你可以通过adb命令将设备切换到“直接启动”模式,但操作相对复杂,需要设备支持且已启用调试。

4. 关注 onUnlocked() 回调 如果你的应用目标API级别在24(Android 7.0)及以上,并且声明了 android:directBootAware=”true”,那么 Application 类会收到 onUnlocked() 回调。这是你安全加载CE区域敏感数据的正确时机。你可以在这里初始化网络模块(需要令牌)、加载用户个人数据等。

5. 备选方案:使用其他存储方式 对于极其简单的、需要在开机时读取的配置(比如一个布尔值或整数),如果觉得管理两套SharedPreferences太麻烦,也可以考虑其他更轻量的、始终可读的存储方式:

  • Settings.System / Settings.Global:如果你的是系统应用,并且配置项具有全局性,可以考虑写入系统设置。但这需要特定权限,且不适用于普通应用。
  • 在DE区域直接读写文件:使用 createDeviceProtectedStorageContext().openFileOutput() 来读写一个自定义的配置文件。这给了你最大的控制权,但失去了SharedPreferences的便捷性。

处理Android设备加密状态下的存储问题,本质上是在安全与可用性之间寻找平衡点。对于大多数普通应用,采用“双Context策略”来按需访问DE和CE存储,是最灵活、最安全的做法。理解“直接启动”模式和文件级加密的原理,是避免掉坑的关键。下次当你看到 SharedPreferences in credential encrypted storage are not available until after user is unlocked 这个错误时,希望你能会心一笑,然后从容地拿出 createDeviceProtectedStorageContext() 这把钥匙。

Android应用数据安全实战:Conceal加密库集成与本地敏感数据保护 在移动应用开发中,数据安全是保障用户隐私和业务合规的基石。其核心原理在于通过加密算法对敏感信息进行转换,确保即使数据被非法获取也无法被直接识别。AES-256-GCM作为一种兼具机密性与完整性的认证加密模式,提供了可靠的技术保障。对于Android开发者而言,选择合适的本地加密方案需要在安全强度、性能开销和开发效率之间取得平衡。Conceal作为Facebook开源的高性能加密库,专为Android平台优化,通过极简的API设计简化了文件与SharedPreferences的加密流程,有效防御本地存储数据被 阅读详情

相关推荐

Android数据加密实战:5大核心问题与解决方案详解

数据加密是保障移动应用安全的核心技术,其核心原理是通过特定算法将明文转换为密文,防止敏感信息泄露。在Android开发中,对称加密算法AES因其高效性成为主流选择,而非对称加密RSA则常用于密钥交换。AES-GCM模式结合了加密与完整性验证,提供了更高的安全性。在实际工程实践中,密钥的安全存储是首要挑战,Android KeyStore提供了硬件级保护方案。同时,初始化向量(IV)的随机生成与安全处理、多线程环境下的Cipher实例管理,以及应对碎片化设备的兼容性策略,都是确保加密功能稳定运行的关键。本文聚

weixin_34414650的博客 376

java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available

报错: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked 、、、、 报错:android.database.sqlite.SQLiteCantOpenDatabaseException: u...

yanwenyuan0304的专栏 6264

Android设备标识符合规存储:从权限最小化到全链路加密实践

设备标识符是移动开发中用于识别设备的关键技术概念,其本质是一串能够唯一标识硬件或软件实例的字符序列。在Android生态中,标识符的工作原理经历了从全局统一到应用隔离的演变,例如Android ID在Android 8.0后变为由应用签名、用户和设备共同决定。这项技术的核心价值在于平衡功能实现与用户隐私保护,确保在广告归因、风险控制等应用场景下合法合规。随着GDPR等全球隐私法规的强化,标识符处理必须遵循“权限最小化”和“目的限定”原则,采用分层存储与加密技术。本文聚焦于通过Android Keystore

weixin_34269583的博客 483

Android启动时使用SharedPreferences报错原因分析(转载请删除括号里的内容)

最近做TV开发,Android 9.0,Launcher应用刚启动时调用SharedPreferences,会出现黑屏或闪屏现象。log报错如下:原因:当设备已开机但用户尚未解锁设备时,Android 7.0 后将在安全的“直接启动”模式下运行,开机阶段读写设备将受影响。

空名先生 2996

Android 12.0 SharedPreferences in credential encrypted storage are not avai

在12.0的系统rom定制化开发中,在开机的过程中,由于某些应用在开机解锁阶段就开始访问查询短信和联系人等功能,所以 会出现抛异常的情况出现,接下来分析下相关的情况,然后来解决这些问题

安卓兼职framework和app工程师的博客 366

Android 10.0 SharedPreferences in credential encrypted storage are not avai

在10.0的系统rom定制化开发中,在开机的过程中,由于某些应用在开机解锁阶段就开始访问查询短信和联系人等功能,所以 会出现抛异常的情况出现,接下来分析下相关的情况,然后来解决这些问题

安卓兼职framework和app工程师的博客 855

DeepSeek-R1-Distill-Qwen-1.5B惊艳效果:围棋局面分析+胜率预测+招法建议

本文介绍了如何在星图GPU平台上自动化部署🐋 DeepSeek-R1-Distill-Qwen-1.5B 本地智能对话助手 (Streamlit 驱动)镜像,实现围棋局面分析、胜率预测与招法建议等专业级推理任务。用户可在单卡RTX 3060上本地运行,无需联网或上传数据,适用于围棋教学、棋局复盘与AI陪练等典型场景。

weixin_36204513的博客 305

Android 13.0 SharedPreferences in credential encrypted storage are not avai

在13.0的系统rom定制化开发中,在开机的过程中,由于某些应用在开机解锁阶段就开始访问查询短信和联系人等功能,所以 会出现抛异常的情况出现,接下来分析下相关的情况,然后来解决这些问题

安卓兼职framework和app工程师的博客 223

Android SharedPreferences数据加密实战:从裸奔到安全存储

在移动应用开发中,数据安全是至关重要的技术环节。SharedPreferences作为Android平台轻量级数据存储的标准方案,其默认明文存储方式存在显著安全风险。通过AES对称加密算法,结合GCM认证加密模式,可以在数据写入文件前进行加密,读取时自动解密,实现透明化的安全增强。这种加密方案在性能与安全性之间取得了良好平衡,特别适用于保护本地存储的访问令牌、用户偏好设置等敏感信息。本文以cheesesquare项目为例,详细解析了如何通过包装器模式构建EncryptedSharedPreferences,

mybwu_com的专栏 372

Python控制tek示波器(1)

如何使用Python来控制tek示波器

m0_47441496的博客 3379

论文阅读——Bayesian Knowledge Fusion(贝叶斯知识融合)

该篇论文解决了不确定环境中的信息融合问题。想象一下,有多位专家针对同一情况构建概率模型,我们希望汇总他们提供的信息。直接合并每个信息可能会遇到几个问题。例如,专家们可能不同意某个事件发生的概率,或者他们可能不同意两个事件之间因果关系的方向(例如,一个人认为 A 导致 B,而另一个人认为 B 导致 A)。他们甚至可能不同意概率网络中一组变量之间的整个依赖结构。

qq_64164071的博客 1497

Android数据安全实战:从SharedPreferences到SQLite的加密与防护

数据持久化是移动应用开发的基础环节,涉及SharedPreferences、SQLite等核心组件的使用。其原理是通过文件或数据库将用户数据存储于设备本地,但默认的明文存储方式存在严重安全风险。在数据安全领域,加密技术是保障信息机密性与完整性的关键手段,尤其对于包含用户隐私和敏感业务数据的应用而言。Android Keystore系统提供了硬件级密钥保护,结合AES-GCM等加密算法,能够有效防止数据在存储环节被窃取。从工程实践角度,开发者需要掌握密钥安全管理、加密算法选型以及性能优化等技能。针对adb s

weixin_34138056的博客 331

聚类教程:cisTopic——来自单细胞表观基因组学数据的顺式调控主题的概率建模

之前运行这步的时候报错了如下: 后来发现原因是Ubuntu自动把R包更新到4.2版本,但是Rstudio版本没有更新,更新Rstudio即可,更新Rstudio的教程看此教程:。

watermel__的博客 1051

Android密码安全存储:SharedPreferences与AES加密实践

键值存储是移动开发中数据持久化的基础技术,Android平台的SharedPreferences提供轻量级的键值对存储方案,通过XML文件实现高效读写。其线程安全特性和简洁API设计使其成为保存用户偏好的首选方案。在密码存储等安全敏感场景中,需要结合AES对称加密算法对数据进行加密处理,再通过Base64编码存储。现代移动应用开发中,数据安全存储方案需要平衡便捷性与安全性,SharedPreferences配合密钥派生函数(PBKDF2)和生物识别验证,可以构建可靠的用户凭证存储体系。本文以登录功能为例,详

weixin_29193359的博客 323

用Multisim和74192芯片复刻经典:手把手教你搭建篮球24秒违例计时器(附仿真文件)

本文详细介绍了如何使用Multisim仿真平台和74192芯片构建篮球24秒违例计时器,涵盖时序逻辑电路设计、核心器件选型、仿真环境搭建及调试技巧。通过分步指导,帮助读者掌握数字电路设计的关键技术,并附赠仿真文件供实践参考。

weixin_27014595的博客 279

基于MATLAB Simulink的电动汽车整车控制仿真

在本文中,我们选择了power_ElectricVehicle示例模型,该模型已经包含了电动汽车的基本组成部分,包括电机、变速器、驱动轮、电池等。例如,使用PID控制器来控制电机的转速,使用ABS控制器来控制车轮的制动力,使用转向控制器来控制车辆的转向角度,使用悬挂控制器来控制车轮的姿态等。通过建立模型和实现控制器,我们可以比较直观地了解电动汽车的工作原理和性能特点,并验证设计的控制系统在不同工况下的效果。而在电动汽车的开发中,整车控制系统是至关重要的一个组成部分,直接影响着电动汽车的性能和性价比。

qq_39605374的博客 1805

Android密码安全存储:SharedPreferences与加密实践

键值存储是移动开发中数据持久化的基础技术,Android平台通过SharedPreferences提供轻量级存储方案。其采用XML文件格式存储键值对数据,通过MODE_PRIVATE实现应用沙盒隔离,保障基础数据安全。在用户认证场景中,密码等敏感信息的存储需要结合加密技术,Android Keystore系统提供硬件级密钥保护,支持AES等加密算法实现数据安全存储。实际开发中,记住密码功能需遵循'存储加密密码+自动登录控制'的双重安全策略,同时处理好多用户支持和数据同步等工程问题。本文以SharedPref

weixin_29191669的博客 301

12S BMS 核心设计:基于 STM32H743+BQ34Z100 的 SOC/SOH 实现与全链路技术解析(上)

本文详细介绍了基于STM32H743和BQ34Z100的12串锂电池管理系统(BMS)设计。该系统采用主控制器+专用电量监测计架构,可实现高精度SOC(±1%)和SOH(±2%)估算,支持36V-50.4V电压范围。硬件设计包括电压/电流采样、温度测量、保护电路等模块,软件实现基于HAL库开发,采用DMA方式提高I2C通信效率。系统通过TI专利的ImpedanceTrack™算法实现三维融合估算,并具备完善的保护功能。该设计适用于电动工具、轻型电动车等中功率设备,具有精度高、响应快。

bother3000的博客 1394

Android 9.0必看:SharedPreferences在直接启动模式下的正确使用姿势

本文深入解析Android 9.0及以上版本中,应用在直接启动模式下访问SharedPreferences时,因设备加密存储机制导致的报错问题。文章从系统安全架构切入,详细阐述了直接启动与凭据加密存储的原理,并提供了动态管理双存储上下文的应用级最佳实践与兼容性适配方案,帮助开发者构建更健壮的应用。

k6l7m8n9的博客 565

Vue3 - Element Plus上传组件el-upload上传多个文件时,让on-change监听事件仅执行一次并获取最终文件列表方法(开启多选文件并手动上传,拿到文件列表统一循环调用接口上传)

vue3,elementplus,Upload上传,el-upload,文件上传组件,上传组件on-change里执行多次,取消自动上传后change重复执行问题,upload上传多个文件后导致on-change执行多次拿不到最终文件列表,Element-plus的upload组件on-change不断执行怎么办,上传多个文件多次执行调用,上传多个文件时如何只执行一次on-change方法拿到最终的文件列表,vue3elementplus上传组件开启多个上传后导致change方法执行好几次,仅调用一

🏆 前端领域优质创作者 1万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Setup SharedPreferences in credential encrypted storage are not available until after user is unloc

Caused by: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked 08:08:00.310 5652 5652 E AndroidRuntime: at andr...

4818

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Tink Android 入门实战:基于 SharedPreferences 与 Android Keystore 的 AEAD 加解密示例全解析

本文围绕 Tink 仓库中的 Android Hello World 示例([java_src/examples/android/helloworld/README.md](https://link.gitcode.com/i/d1ac1663b9ee59866bee832f7c3fa110))展开,逐层剖析一个真实可运行的 Android 应用:它用 Tink 完成字符串的加密与解密,密钥存放在

gitblog_00582的博客 602

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Android-Goldfinger 避坑指南:Oreo 密钥失效等已知问题详解

Android-Goldfinger 是一个用于简化 Android 生物识别(指纹/人脸)认证集成的开源库,用几行代码就能完成加密、解密和认证。但很多开发者在使用 Android-Goldfinger 时,会遇到 Oreo 设备上密钥突然失效、解密失败、指纹变更后无法认证等"玄学"问题。这篇文章就带你系统梳理 Android-Goldfinger 的已知问题与避坑方案,让生物识别认证集成少走弯路

gitblog_00821的博客 1143
上一篇: Windows系统IPv6网络连接故障排查与修复指南
下一篇: CSS进阶:多行文本溢出隐藏与省略号显示的优雅解决方案
python9snake
博客等级 码龄1天 0粉丝 1724原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值