【Android Developers Training】 23. 序言:保存数据

本文介绍了Android应用程序中的几种数据存储方式,包括使用SharedPreferences保存简单的键值对数据、在文件系统中存储任意文件以及利用SQLite数据库进行数据管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/basics/data-storage/index.html


大多数的Android应用都需要保存数据,甚至仅仅在onPause()中存储应用状态信息防止用户进度丢失,这也属于数据的存储。大多数较复杂的应用也需要存储用户的设置信息,一些应用必须在文件和数据库中管理大量数据。这一系列课程将向你介绍Android中主要的几个存储数据方案,包括:

  • 以键值对的方式存储简单的数据类型于应用配置文件(shared preferences file)中。
  • 在Android文件系统中存储任意文件
  • 通过SQLite管理和使用数据库

 

课程:

【Android Developers Training】 24. 保存键值对

【Android Developers Training】 25. 保存文件

【Android Developers Training】 26. 在SQL数据库中保存数据

转载于:https://www.cnblogs.com/jdneo/p/3462860.html

还是会报Process crashed before executing the test(s): android.content.res.Resources$NotFoundException: String resource ID #0x0 at android.content.res.Resources.getText(Resources.java:466) at android.content.res.Resources.getString(Resources.java:559) at android.content.Context.getString(Context.java:946) at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:217) at androidx.startup.AppInitializer.discoverAndInitialize(AppInitializer.java:207) at androidx.startup.InitializationProvider.onCreate(InitializationProvider.java:49) at android.content.ContentProvider.attachInfo(ContentProvider.java:2644) at android.content.ContentProvider.attachInfo(ContentProvider.java:2613) at android.app.ActivityThread.installProvider(ActivityThread.java:8292) at android.app.ActivityThread.installContentProviders(ActivityThread.java:7807) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7488) at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2416) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loopOnce(Looper.java:232) at android.os.Looper.loop(Looper.java:317) at android.app.ActivityThread.main(ActivityThread.java:8705) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:913) INSTRUMENTATION_RESULT: shortMsg=Process crashed. INSTRUMENTATION_CODE: 0
07-31
<think>我们正在处理一个在运行Android单元测试APK时出现的崩溃问题,即使测试类是空的,也会报错: `android.content.res.Resources$NotFoundException: String resource ID #0x0` 根据错误堆栈,问题发生在`androidx.startup.AppInitializer`中,具体是在尝试初始化某些组件时,获取一个字符串资源,但资源ID为0,导致找不到资源。 结合之前的分析,我们重点考虑测试APK的资源打包和初始化问题。同时,注意到错误发生在`androidx.startup`库,这可能是由于AndroidX的App Startup库在初始化时出现了问题。 以下步骤将帮助解决此问题: ### 1. **检查是否在测试中禁用了AndroidX Startup初始化** 错误堆栈指向`InitializationProvider`(即App Startup库)在初始化时崩溃。在测试中,我们可能不需要启动这些初始化,因此可以尝试在测试的AndroidManifest.xml中禁用App Startup。 在`src/androidTest/AndroidManifest.xml`文件中,添加以下内容覆盖主清单中的`InitializationProvider`: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="your.package.name.test"> <application> <!-- 禁用AndroidX Startup初始化 --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="remove" /> </application> </manifest> ``` 这样,测试APK中就不会初始化App Startup,从而避免在测试启动时调用该初始化。 ### 2. **确保测试模块包含必要的资源** 在测试模块的`build.gradle`中,确保启用了包含Android资源: ```groovy android { testOptions { unitTests { includeAndroidResources = true } } } ``` ### 3. **检查资源ID冲突** 资源ID为0通常表示资源查找失败,可能是因为资源ID不存在或者为无效值。检查项目中是否有资源ID被错误地设置为0的情况。这可能是由于资源合并冲突或资源编译错误导致的。 尝试清理并重新构建项目: ```bash ./gradlew clean ./gradlew assembleDebugAndroidTest ``` ### 4. **更新AndroidX库** 确保使用的AndroidX库版本一致且最新,特别是`androidx.startup`库。在`build.gradle`中检查: ```groovy dependencies { implementation "androidx.startup:startup-runtime:1.1.1" // ... 其他依赖 } ``` 使用最新版本(截至当前是1.1.1)以避免已知问题。 ### 5. **检查测试设备或模拟器的资源** 如果设备或模拟器内存不足,也可能导致资源加载失败。确保设备有足够的内存,并尝试重启设备或模拟器。 ### 6. **创建最小化测试用例** 创建一个最简单的测试类,只包含一个空测试,并检查是否仍然崩溃: ```java import org.junit.Test; public class ExampleTest { @Test public void emptyTest() { // 空测试 } } ``` 如果这个空测试仍然崩溃,则说明问题不在测试代码本身,而是环境配置问题。 ### 7. **检查日志** 在运行测试时,使用`adb logcat`捕获完整的日志,可能会提供更多线索。在崩溃发生前后,注意是否有其他相关错误或警告。 ### 8. **禁用即时运行(Instant Run)** 如果使用Android Studio,尝试禁用Instant Run,因为它有时会干扰测试。 ### 9. **检查ProGuard/R8配置** 如果启用了代码混淆,确保测试APK没有被过度混淆。在测试构建类型中禁用混淆: ```groovy android { buildTypes { debug { minifyEnabled false shrinkResources false } } } ``` ### 10. **使用诊断工具** 使用`aapt`工具检查测试APK中的资源: ```bash aapt dump resources path/to/your-debug-androidTest.apk ``` 查看资源表中是否有异常,特别是字符串资源。 ### 总结步骤 1. **在测试清单中移除`InitializationProvider`**(关键步骤,因为错误堆栈指向这里) 2. 确保`includeAndroidResources = true` 3. 清理并重建项目 4. 更新AndroidX库 如果以上步骤仍然不能解决问题,请尝试在项目中创建一个全新的模块,只包含最简单的测试,逐步排查是项目配置问题还是环境问题。 ### 相关问题 1. 如何在Android单元测试中禁用特定的ContentProvider? 2. AndroidX Startup库的工作原理是什么? 3. 如何解决资源ID冲突问题? 4. 如何检查测试APK中的资源? 5. 单元测试中常见的资源加载失败原因有哪些?[^1] [^1]: Android Developers官方文档: 测试中的资源处理</think>### 解决 Android 单元测试 APK 崩溃问题分析 根据错误日志 `Resources$NotFoundException: String resource ID #0x0`,即使测试类为空也会崩溃,这表明问题发生在测试环境初始化阶段而非测试本身。结合引用[1]中提到的资源处理机制,以下是系统性解决方案: --- #### 核心问题定位 1. **资源初始化失败** `AppInitializer` 在初始化阶段尝试获取字符串资源时失败(ID=0x0) 2. **测试 APK 资源未正确打包** 测试 APK 未包含主模块的资源文件(特别是 `strings.xml`) 3. **AndroidX Startup 库兼容性问题** `androidx.startup.InitializationProvider` 初始化时依赖的资源缺失 --- #### 解决方案 ##### 1. 强制包含 Android 资源(关键步骤) 在模块的 `build.gradle` 中启用资源包含: ```groovy android { testOptions { unitTests { includeAndroidResources = true // 必须启用 returnDefaultValues = true // 防止空资源 } } } ``` ##### 2. 禁用 AndroidX Startup 初始化 在测试清单 `src/androidTest/AndroidManifest.xml` 中添加: ```xml <application> <!-- 禁用 Startup 初始化 --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> </application> ``` ##### 3. 验证资源打包 创建资源检查测试类: ```java @RunWith(AndroidJUnit4.class) public class ResourceValidationTest { @Test public void verifyResources() { Context ctx = ApplicationProvider.getApplicationContext(); // 验证基础资源存在 assertNotEquals(0, R.string.app_name); // 验证资源可访问 assertNotNull(ctx.getString(R.string.app_name)); } } ``` ##### 4. 清理构建缓存 ```bash # 清理项目缓存 ./gradlew clean # 删除 Gradle 全局缓存 rm -rf ~/.gradle/caches/ # 重新构建测试 APK ./gradlew assembleDebugAndroidTest --info ``` ##### 5. 更新测试依赖 ```groovy dependencies { // 使用最新稳定版 androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' implementation 'androidx.startup:startup-runtime:1.2.0' // 修复初始化问题 } ``` ##### 6. 内存优化(参考引用[1]) 在 `gradle.properties` 中添加: ```properties # 增加 Gradle 内存 org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m # 启用构建缓存 android.enableBuildCache=true ``` --- #### 技术原理说明 1. **资源 ID 0x0 的含义** 当系统尝试获取 `@string/` 资源但找不到对应 ID 时,会回退到 ID=0x0 的虚拟资源,导致崩溃 2. **`includeAndroidResources` 的作用** $$ \text{test APK} = \text{main resources} \oplus \text{test resources} $$ 该标记确保主模块资源被合并到测试 APK 3. **Startup 库初始化流程** ```mermaid graph TD A[ContentProvider.onCreate] --> B[AppInitializer.discoverAndInitialize] B --> C[读取初始化的组件清单] C --> D[加载组件依赖的资源] D --> E{资源是否存在?} E -->|否| F[抛出Resources$NotFoundException] ``` --- #### 验证步骤 1. 检查测试 APK 资源: ```bash aapt dump resources app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk | grep 'string/app_name' ``` 2. 运行最小测试: ```java @RunWith(AndroidJUnit4.class) public class SanityTest { @Test public void emptyTest() { // 仅验证测试框架启动 assertTrue(true); } } ``` --- #### 相关问题 1. 如何为 Android 测试模块配置独立资源? 2. 如何诊断测试 APK 中的资源打包问题? 3. `androidx.startup` 库的初始化机制是什么? 4. 单元测试中常见的资源加载失败原因有哪些?[^1] 5. 如何优化 Android 测试的内存使用?[^1] [^1]: Android 官方构建问题文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值