AndroidTest写入外部存储失败求助:权限授予方案均无效
嘿,我太懂你在不同设备上折腾外部存储测试的痛苦了——我之前也踩过一模一样的坑,尤其是权限规则在不同API等级下的差异,简直让人头大。咱们一步步拆解问题,把复杂的情况简化,找到能稳定跑通的方案:
1. 先把基础配置补全,别漏了测试清单
很多人会忽略测试模块的权限声明!你需要在两个地方加权限:
- 主项目的
AndroidManifest.xml:针对不同API等级声明对应权限,比如API<30加WRITE_EXTERNAL_STORAGE,API30+如果要访问公共目录,得加MANAGE_EXTERNAL_STORAGE(不过这个权限没法自动授予); - 测试模块的
src/androidTest/AndroidManifest.xml:同步声明相同的权限,否则测试进程拿不到权限。
另外,针对Android 10-11(API29-30),如果想暂时绕过Scoped Storage,要在主清单的application标签里加:
android:requestLegacyExternalStorage="true"
2. GrantPermissionRule的正确打开方式(以及它的局限性)
GrantPermissionRule确实能帮我们自动授予危险权限,但它有个硬伤:搞不定Android 11+的MANAGE_EXTERNAL_STORAGE这种特殊权限,这种权限必须用户手动去设置里开启。
正确的写法要分API等级适配:
@Rule public GrantPermissionRule permissionRule = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) : GrantPermissionRule.grant(); // API31+不需要WRITE权限,或者改用Scoped Storage
3. 别死磕公共目录!应用专属存储才是测试的救星
从Android 10开始,应用专属外部存储(比如getExternalFilesDir())不需要任何权限,而且在所有设备上都能稳定写入,完全不用纠结权限弹窗或者设备差异。
如果你的测试只是验证写入可行性,优先用这个路径,代码简单还稳:
File testFile = new File(getInstrumentation().getTargetContext().getExternalFilesDir(null), "test_demo.txt");
4. 自定义权限授予器的坑你可能踩了
你说的自定义权限授予器应该是用UiAutomator模拟点击权限弹窗吧?这个方案的问题在于:不同品牌设备的权限弹窗UI完全不一样(比如Pixel和华为的按钮布局、文字都不同),这就是为什么你在多设备上测试失败的原因。
除非你专门针对每款设备做适配,否则不建议用这种方式,太不稳定了。
5. 无Activity项目的测试注意点
因为你的项目没有Activity,测试时要确保用对Context:别用getActivity(),而是用getInstrumentation().getTargetContext(),否则可能因为Context错误导致存储路径无效。
最简可运行测试用例
给你一个能覆盖绝大多数设备的测试代码,直接复制就能跑:
@RunWith(AndroidJUnit4.class) public class ExternalStorageWriteTest { @Rule public GrantPermissionRule permissionRule = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) : GrantPermissionRule.grant(); @Test public void testWriteToStorage() throws IOException { Context context = getInstrumentation().getTargetContext(); File testFile; // 分API等级选择存储路径 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // API31+用应用专属存储,无需权限 testFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "test_write_success.txt"); } else { // 旧版本用公共目录,需要权限 testFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "test_write_success.txt"); } // 写入内容 FileWriter writer = new FileWriter(testFile); writer.write("Hello Storage Test!"); writer.close(); // 验证写入成功 assertTrue("测试文件未创建成功", testFile.exists()); String content = new String(Files.readAllBytes(testFile.toPath())); assertEquals("写入内容不匹配", "Hello Storage Test!", content); // 清理测试文件 testFile.delete(); } }
总结一下:优先用应用专属存储做测试,避免权限问题;如果必须测试公共目录,一定要针对不同API等级做适配,别指望一套代码跑通所有设备。
内容的提问来源于stack exchange,提问作者Rafael T

