BitmapFactory:无法解码流:java.io.FileNotFoundException:打开失败:Android 上的 EACCES(权限被拒绝)Q
BitmapFactory: Unable to decode stream: java.io.FileNotFoundException:open failed: EACCES (Permission denied) on Android Q
我授予了权限但是 BitmapFactory.decodeFile()
returns 这个错误:
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20200130_165131.jpg: open failed: EACCES (Permission denied)
我想从图库中取出最后一张图片并将其保存到另一个文件中。我在 运行 上的 Android P 测试设备上对其进行了测试。但是 returns 这个错误在 android 模拟器 android 版本 Q(API 级别 29)。这有什么问题吗?
这些是我的代码片段:
writeStoragePermissionGranted();
BitmapFactory.Options options = null;
Bitmap bm = null;
String[] projection = new String[]{
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
};
final Cursor cursor = this.getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) {
final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
String imageLocation = cursor.getString(1);
File imageFile = new File(imageLocation);
if (imageFile.exists()) {
options = new BitmapFactory.Options();
options.inSampleSize = 2;
try {
bm = BitmapFactory.decodeFile(imageLocation, options);
} catch(Exception e) {
e.printStackTrace();
}
}
imageView.setImageBitmap(bm);
try {
saveImage(bm,"NAME" );
} catch (IOException e) {
e.printStackTrace();
}
}
writeStoragePermissionGranted()
函数:
public void writeStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
startPeriodicRequest();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION);
}
} else {
return;
}
}
和清单权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
我知道对此有很多疑问。但是没有人在 android 模拟器版本 Q 上工作。另一方面,这段代码在 android pie (API level 28)
上工作
在 Android 10,他们引入了 "Scoped Storage" 的概念,这样一来,您就无法再使用其路径打开图像。您可以获得更多信息 HERE.
所以现在你必须使用它的 ParcelFileDescriptor
和它的 Uri
来解码它。
您可以:
final Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) {
final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
if (Build.VERSION.SDK_INT >= 29) {
// You can replace '0' by 'cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)'
// Note that now, you read the column '_ID' and not the column 'DATA'
Uri imageUri= ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getInt(0));
// now that you have the media URI, you can decode it to a bitmap
try (ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(mediaUri, "r")) {
if (pfd != null) {
bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
}
} catch (IOException ex) {
}
} else {
// Repeat the code you already are using
}
}
将 android:requestLegacyExternalStorage="true"
添加到 manifest 的应用程序块实际上可以解决问题![=11=]
您只需将 android:requestLegacyExternalStorage="true"
添加到 Manifest.xml
中的 Application
标签。
希望下面的代码能帮到你。
Manifest.xml
在清单文件中添加以下内容:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
另加:
在 Manifest 的 Application 标签中添加这个。
android:requestLegacyExternalStorage="true"
打开图库获取图片:
private void openImagePicker() {
MyDatePicker datePicker = new MyDatePicker();
datePicker.show(getSupportFragmentManager(), "DATE PICK");
}
启动 ActivityForResult:
onActivityResult
你会得到结果对象,你会得到图像原始 uri。
ActivityResultLauncher<Intent> galleryResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
imageURi = data.getData();
image.setVisibility(View.VISIBLE);
String path = getPath(imageURi);
File file = new File(path);
if (file.exists()) {
Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
image.setImageBitmap(myBitmap);
}
}
}
});
获取图片的绝对路径:
public String getPath(Uri uri) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} else return null;
}
我授予了权限但是 BitmapFactory.decodeFile()
returns 这个错误:
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20200130_165131.jpg: open failed: EACCES (Permission denied)
我想从图库中取出最后一张图片并将其保存到另一个文件中。我在 运行 上的 Android P 测试设备上对其进行了测试。但是 returns 这个错误在 android 模拟器 android 版本 Q(API 级别 29)。这有什么问题吗?
这些是我的代码片段:
writeStoragePermissionGranted();
BitmapFactory.Options options = null;
Bitmap bm = null;
String[] projection = new String[]{
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
};
final Cursor cursor = this.getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) {
final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
String imageLocation = cursor.getString(1);
File imageFile = new File(imageLocation);
if (imageFile.exists()) {
options = new BitmapFactory.Options();
options.inSampleSize = 2;
try {
bm = BitmapFactory.decodeFile(imageLocation, options);
} catch(Exception e) {
e.printStackTrace();
}
}
imageView.setImageBitmap(bm);
try {
saveImage(bm,"NAME" );
} catch (IOException e) {
e.printStackTrace();
}
}
writeStoragePermissionGranted()
函数:
public void writeStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
startPeriodicRequest();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION);
}
} else {
return;
}
}
和清单权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
我知道对此有很多疑问。但是没有人在 android 模拟器版本 Q 上工作。另一方面,这段代码在 android pie (API level 28)
上工作在 Android 10,他们引入了 "Scoped Storage" 的概念,这样一来,您就无法再使用其路径打开图像。您可以获得更多信息 HERE.
所以现在你必须使用它的 ParcelFileDescriptor
和它的 Uri
来解码它。
您可以:
final Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) {
final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
if (Build.VERSION.SDK_INT >= 29) {
// You can replace '0' by 'cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)'
// Note that now, you read the column '_ID' and not the column 'DATA'
Uri imageUri= ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getInt(0));
// now that you have the media URI, you can decode it to a bitmap
try (ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(mediaUri, "r")) {
if (pfd != null) {
bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
}
} catch (IOException ex) {
}
} else {
// Repeat the code you already are using
}
}
将 android:requestLegacyExternalStorage="true"
添加到 manifest 的应用程序块实际上可以解决问题![=11=]
您只需将 android:requestLegacyExternalStorage="true"
添加到 Manifest.xml
中的 Application
标签。
希望下面的代码能帮到你。
Manifest.xml
在清单文件中添加以下内容:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
另加:
在 Manifest 的 Application 标签中添加这个。
android:requestLegacyExternalStorage="true"
打开图库获取图片:
private void openImagePicker() {
MyDatePicker datePicker = new MyDatePicker();
datePicker.show(getSupportFragmentManager(), "DATE PICK");
}
启动 ActivityForResult:
onActivityResult
你会得到结果对象,你会得到图像原始 uri。
ActivityResultLauncher<Intent> galleryResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
imageURi = data.getData();
image.setVisibility(View.VISIBLE);
String path = getPath(imageURi);
File file = new File(path);
if (file.exists()) {
Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
image.setImageBitmap(myBitmap);
}
}
}
});
获取图片的绝对路径:
public String getPath(Uri uri) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} else return null;
}