【问题标题】:Why do I need the WRITE_EXTERNAL_STORAGE permission with getExternalCacheDir() on Android Lollipop?为什么我需要在 Android Lollipop 上使用 getExternalCacheDir() 获得 WRITE_EXTERNAL_STORAGE 权限?
【发布时间】:2015-01-16 23:01:48
【问题描述】:

我的应用程序将缓存文件写入(和读取)到 getExternalCacheDir() 位置。在 Android Lollipop (API 21) 之前,我一直在成功使用此权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />

存在 maxSdkVersion 是因为在 API v18 之后不需要此权限: http://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE

但在 Android Lollipop (5.0) 上,我获得了这样的访问权限(我的日志输出显示使用的实际路径):

11-19 13:01:59.257    4462-4541/com.murrayc.galaxyzoo.app E/android-galaxyzoo﹕     createCacheFile(): IOException for filename=/storage/emulated/0/Android/data/com.murrayc.galaxyzoo.app/cache/52
    java.io.IOException: open failed: EACCES (Permission denied)
            at java.io.File.createNewFile(File.java:941)
            at com.murrayc.galaxyzoo.app.provider.ItemsContentProvider.createCacheFile(ItemsContentProvider.java:528)

我在模拟器和我的 Nexus 4 上都看到了这一点。是发生了什么变化,还是我一直做错了什么?我只想访问我自己的应用程序的缓存。

更新:我现在只能在我的设备上看到这个(运行标准 Android 5.1.1 的 Nexus 4,自从我第一次遇到这个问题以来,它甚至有一个全新的 Android 刷新)。我不再在模拟器中看到这个 - 当然系统图像已经更新了好几次。

【问题讨论】:

  • 如果你不添加这个 android:maxSdkVersion="18" 属性那么它可以工作吗?
  • 是的。我必须始终请求 WRITE_EXTERNAL_STORAGE 才能完成这项工作。我想知道是否有办法说 20 需要它,如果 21 真的需要它(棒棒糖)。
  • 是的,根据文档从 API 级别 19 开始,读取/写入 getExternalFilesDir(String) 和 getExternalCacheDir() 返回的应用程序特定目录中的文件不需要此权限,因此我们不需要此权限19 或以上的 API 但没有此权限,结论无效。
  • 嗨@murrayc,当我将我的应用程序定位到Masmallow 时,我打算使用&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" /&gt;。我在 Masmallow 中测试过,效果很好。你还在 KitKat & Lollipop 中看到这个问题吗?
  • 是的,我仍然在 Lollipop 中看到这个问题。 Lollipop 没有任何改变。它似乎在 Marshmallow 中修复了,但 Lollipop 就在那里,我们必须处理它。

标签: android android-file android-permissions


【解决方案1】:

我们在 Nexus 5 上的 API 21 (Lollipop) 上看到了相同的行为:

java.io.FileNotFoundException: /storage/emulated/0/Android/data/[package name]/cache/http/journal.tmp: open failed: EACCES (Permission denied)
   at libcore.io.IoBridge.open(IoBridge.java:456)
   at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
   at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
   at com.android.okhttp.internal.DiskLruCache.rebuildJournal(DiskLruCache.java:346)
   at com.android.okhttp.internal.DiskLruCache.open(DiskLruCache.java:239)
   at com.android.okhttp.HttpResponseCache.<init>(HttpResponseCache.java:140)
   at android.net.http.HttpResponseCache.install(HttpResponseCache.java:199)
...
   at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1011)
   at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4518)
   at android.app.ActivityThread.access$1500(ActivityThread.java:144)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1339)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5221)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
   at libcore.io.Posix.open(Posix.java)
   at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
   at libcore.io.IoBridge.open(IoBridge.java:442)
   at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
   at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
   at com.android.okhttp.internal.DiskLruCache.rebuildJournal(DiskLruCache.java:346)
   at com.android.okhttp.internal.DiskLruCache.open(DiskLruCache.java:239)
   at com.android.okhttp.HttpResponseCache.<init>(HttpResponseCache.java:140)
   at android.net.http.HttpResponseCache.install(HttpResponseCache.java:199)
...
   at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1011)
   at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4518)
   at android.app.ActivityThread.access$1500(ActivityThread.java:144)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1339)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5221)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

在 Google 将 Android 5.0 推送到 AOSP 之前,我们无法确定它是否是一个错误或故意(但未记录)的更改,但无论如何我已经提出了这个错误: https://code.google.com/p/android/issues/detail?id=81357

添加 WRITE_EXTERNAL_STORAGE 权限可防止引发上述异常,但需要最终用户权限才能升级现有应用。由于我们的应用不使用此权限并且我们不想添加它,因此我们将回退到对除 KitKat 设备之外的所有设备使用内部缓存。

顺便说一句,我发现这是 KitKat 中引入的更改的有趣背景:http://www.doubleencore.com/2014/03/android-external-storage/

【讨论】:

  • 感谢您确认我不是唯一遇到此问题的人,但这似乎只是对问题的重述,而不是答案。
  • @murrayc 我也向 Google 提出了这个错误,当然,现在修复 Lollipop 为时已晚,因为它已经在野外了。仅使用内部缓存的后备方案足以满足我们的需求。
  • 好的,谢谢。你有什么迹象表明他们修好了,为时已晚?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 2013-08-28
  • 1970-01-01
  • 2015-06-08
  • 2011-02-10
  • 2015-08-06
相关资源
最近更新 更多