我在某些设备上存储访问时遇到了一个非常奇怪的问题。该应用程序适用于我的测试设备(Nexus 4& 7,Samsung GS5)。我的所有设备都运行Android 4.4.2。但是我收到了很多用户的电子邮件,说应用程序无法写入存储(内部存储和SD卡)。从用户反馈收到的日志文件中,我可以看到问题是以下代码:
try {
if (fStream == null) {
fStream = new FileOutputStream(filename, true);
}
fStream.write(data, 0, bytes);
return;
} catch (IOException ex) {
ex.printStackTrace();
}
它在fStream = new FileOutputStream(filename,true)行抛出异常;在创建FileOutputStream时。
堆栈日志是:
W/System.err( 8147): Caused by: java.io.FileNotFoundException: /storage/emulated/0/my_folder/test_file_name.png: open failed: EACCES (Permission denied)
w/System.err( 8147): at libcore.io.IoBridge.open(IoBridge.java:409)
W/System.err( 8147): at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
W/System.err( 8147): at java.io.FileOutputStream.<init>(FileOutputStream.java:128)
W/System.err( 8147): at myapp.save(SourceFile:515)
W/System.err( 8147): ... 8 more
W/System.err( 8147): Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
W/System.err( 8147): at libcore.io.Posix.open(Native Method)
W/System.err( 8147): at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
W/System.err( 8147): at libcore.io.IoBridge.open(IoBridge.java:393)
W/System.err( 8147): ... 11 more
在AndroidManifest.xml中,我声明了以下权限:
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
我已确认用户正在SD卡上使用正确的应用程序专用。而更奇怪的是,它也无法写入内部存储。如果我同时阅读和阅读,怎么会发生这种情况?写权限?用户表示他们当时没有将他们的设备连接到PC。
更新
事实证明我调用open并过于频繁地关闭FileOutputStream,这会在某些时候抛出FileNotFoundException。听起来更像是一个线程问题。
答案 0 :(得分:121)
对于API 23+,您需要请求读/写权限,即使它们已经在您的清单中。
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* @param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
的AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
答案 1 :(得分:32)
我不久前遇到了类似的问题。
您的问题可能出在两个不同的领域。这是你要创建要写入的文件的方式,或者你的写作方法可能存在缺陷,因为它依赖于手机。
如果您要将文件写入SD卡上的特定位置,请尝试使用环境变量。他们应该总是指向一个有效的位置。这是写入下载文件夹的示例:
java.io.File xmlFile = new java.io.File(Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ "/Filename.xml");
如果您正在将文件写入应用程序的内部存储。试试这个例子:
java.io.File xmlFile = new java.io.File((getActivity()
.getApplicationContext().getFileStreamPath("FileName.xml")
.getPath()));
我个人依赖外部库来处理流式文件。这个还没有让我失望。
org.apache.commons.io.FileUtils.copyInputStreamToFile(is, file);
我在失败的写入命令上丢失了太多次数据,所以我依靠着名且经过测试的库来解决我的IO繁重问题。
如果文件很大,您可能还想查看在后台运行IO,或使用回调。
如果您已经在使用环境变量,则可能是权限问题。查看Justin Fiedler的答案。
答案 2 :(得分:25)
面向 Android Q-API 29 的应用会获得过滤到外部存储设备的视图。快速解决方案是在AndroidManifest.xml中添加以下代码:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
在此处阅读有关此内容的更多信息: https://developer.android.com/preview/privacy/scoped-storage
答案 3 :(得分:6)
就我而言,我的错误案例
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android.permission
必须是小写的,并且不知何故整个字符串在我们的源代码中都是大写的。
答案 4 :(得分:5)
我也面临同样的问题。 经过大量的努力,我发现了我的错误。 我的设备通过USB线连接到计算机。 有大量存储类型,媒体设备(MTP),相机(PTP)等USB连接类型。 我的连接类型是 - &#39; Mass Storage&#39;,这导致了问题。 当我更改连接类型时,问题就解决了。
在Android设备上访问文件系统时始终记住: -
不要将大量存储设备连接到计算机/电脑上。
答案 5 :(得分:2)
我遇到了同样的问题,发现我必须在运行时请求权限,即使我已经在清单中声明了它。就像Justin Fiedler的回答一样。
有关此内容的官方文档如下:https://developer.android.com/training/permissions/requesting.html
我的实现与Justin Fiedler的回答略有不同,它还实现了v4片段的onRequestPermissionsResult方法来处理权限请求响应。
public static final int REQUEST_EXTERNAL_PERMISSION_CODE = 666;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public static final String[] PERMISSIONS_EXTERNAL_STORAGE = {
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE
};
public boolean checkExternalStoragePermission(Activity activity) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
return true;
}
int readStoragePermissionState = ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE);
int writeStoragePermissionState = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
boolean externalStoragePermissionGranted = readStoragePermissionState == PackageManager.PERMISSION_GRANTED &&
writeStoragePermissionState == PackageManager.PERMISSION_GRANTED;
if (!externalStoragePermissionGranted) {
requestPermissions(PERMISSIONS_EXTERNAL_STORAGE, REQUEST_EXTERNAL_PERMISSION_CODE);
}
return externalStoragePermissionGranted;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_EXTERNAL_PERMISSION_CODE) {
if (checkExternalStoragePermission(getActivity())) {
// Continue with your action after permission request succeed
}
}
}
}
答案 6 :(得分:2)
首先提供或检查权限,如
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
如果这两个权限都正常,请检查输出流的格式是否正确。
示例:
FileOutputStream fos=new FileOutputStream(Environment.getExternalStorageDirectory()+"/rahul1.jpg");
答案 7 :(得分:2)
就我而言,这是权限问题。 问题是,在使用Android 4.0.4的设备上,我可以访问文件,没有任何错误或异常。在使用Android 5.1的设备上,它因ACCESS异常而失败(打开失败:EACCES(Permission denied))。 通过向清单文件添加以下权限来处理它:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
所以我猜这是OS版本中权限管理导致失败的区别。
答案 8 :(得分:1)
我遇到了同样的问题但是 有时,最困难的问题得到简单的答案。
我重新检查明显的permisions并且WAS_NOT写了permision 我的耻辱!!!
答案 9 :(得分:1)
就我而言,我在android:isolatedProcess="true"
中使用service
选项作为AndroidManifest.xml
。
我一删除它,错误就消失了......
答案 10 :(得分:1)
我能够通过删除来解决这个问题
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
这一行来自
uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" tools:ignore="ScopedStorage"
而且您不再需要读取权限
答案 11 :(得分:0)
如果客户使用的是Android 6.0,Android会为(Marshmallow)添加新的permission model。
诀窍:如果您的目标是22或更低版本,您的应用程序将在安装时请求所有权限,就像在Marshmallow下运行操作系统的任何设备上一样
答案 12 :(得分:0)
在我的情况下,问题是静态的WIFI配置与使用相同IP地址的其他设备发生冲突。
答案 13 :(得分:0)
我也找到了解决方法。
在启动应用程序之前,我将root授予了文件浏览器,并且在退出应用程序时未禁用写入/读取权限。
当我使用restrat设备重置所有权限时,我的应用程序无法使用外部存储器。
答案 14 :(得分:0)
@Uriel Frankel是正确的,Android 10存储访问权限已更改。但是正确的方法不是使用旧存储标志,而是请求存储应用,如下所示:
val screenShotDirPath = getApplication<Application>().getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.path
getExternalFilesDir 是您所需要的。
答案 15 :(得分:0)
添加这两个读写权限,以解决此问题
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
将此行添加到“应用程序”标记中
android:requestLegacyExternalStorage="true"
答案 16 :(得分:0)
我使用另一条路径解决了这个问题:
data/data/[packagename]/cache/...
答案 17 :(得分:0)
对于那些尝试了上述所有解决方案但仍然无法解决 API 30 错误的人。
在您的 targetSdkVersion
30
中将 29
defaultConfig
更改为 build.gradle(app)
代码看起来像:-
defaultConfig {
applicationId "com.example.company"
minSdkVersion 16
targetSdkVersion 29
.
.
.
}
答案 18 :(得分:0)
我遇到了同样的问题。我的应用在 Android 11 上运行,但在 Android 10 中出现错误。升级到 Android 11 解决了问题。
答案 19 :(得分:-2)
在我的情况下,我忘了添加/在文件名前添加我摆脱它
bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(Environment.getExternalStorageDirectory()+"/arjunreddy.png"));