且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为什么我需要在 Android Lollipop 上通过 getExternalCacheDir() 获得 WRITE_EXTERNAL_STORAGE 权限?

更新时间:2022-11-16 23:25:37

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

We've seen same behaviour on API 21 (Lollipop) on a Nexus 5:

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

Until Google push Android 5.0 to the AOSP we won't be able to work out if its a bug or deliberate (but undocumented) change, but I've raised this bug regardless: https://code.google.com/p/android/issues/detail?id=81357

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

Adding WRITE_EXTERNAL_STORAGE permission prevents the above exception being thrown, but will require end user permission to upgrade existing apps. Since our app doesn't use this permission and we don't want to add it, we're falling back to using internal cache for all except KitKat devices.

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

As an aside, I found this an interesting background to the changes introduced in KitKat: http://www.doubleencore.com/2014/03/android-external-storage/