Android - 选取图像时无法使用相同的 URI 两次
Android - unable to use same URI twice when picking an image
我正在使用照片裁剪库(此处为 2.7 版:https://github.com/ArthurHub/Android-Image-Cropper)使用图库应用程序(如三星图库、google 照片、文件等)挑选和裁剪图像。
该库通过使用 Intent.ACTION_GET_CONTENT 启动选择器意图来工作,并且在用户选择图像后,Uri 被传递到裁剪视图,从而打开位图。
在某些情况下,我需要通过自动使用用户最后选择的照片中的 Uri 来重置裁剪。因此,在用户选择图像后,我记住 Uri,然后使用相同的 Uri 开始裁剪步骤(解码位图)。
这适用于 Samsung Gallery,但我对 Google 照片有问题,当重新打开 uri 时会抛出 SecurityException。因此,只有在用户第一次选择图像时,使用 google 张照片的图像选择才能正常工作。当我尝试传递相同的 Uri 时(当我重置作物时),我得到下面的异常(这也发生在 "Files" - 我在模拟器和摩托罗拉上测试过):
java.lang.RuntimeException: Failed to load sampled bitmap: content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253Ace62f6ca-6054-486b-bbb1-b6145ac50272/ORIGINAL/NONE/27402515
Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at com.theartofdev.edmodo.cropper.BitmapUtils.decodeSampledBitmap(BitmapUtils.java:130)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:73)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:24)
at android.os.AsyncTask.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at android.os.Parcel.createException(Parcel.java:1950)
at android.os.Parcel.readException(Parcel.java:1918)
at android.os.Parcel.readException(Parcel.java:1868)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:4118)
用于向用户显示选择照片的应用程序的代码是:
List<Intent> allIntents = new ArrayList<>();
PackageManager packageManager = this.getPackageManager();
boolean includeCamera = useCamera;
// collect all camera intents if Camera permission is available
if (!CropImage.isExplicitCameraPermissionRequired(this) && includeCamera) {
allIntents.addAll(CropImage.getCameraIntents(this, packageManager));
}
boolean includeDocuments = false;
if (useGallery) {
List<Intent> galleryIntents =
CropImage.getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments);
if (galleryIntents.size() == 0) {
// if no intents found for get-content try pick intent action (Huawei P9).
galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_PICK, includeDocuments);
}
allIntents.addAll(galleryIntents);
}
Intent target;
if (allIntents.isEmpty()) {
target = new Intent();
} else {
target = allIntents.get(allIntents.size() - 1);
allIntents.remove(allIntents.size() - 1);
}
// Create a chooser from the main intent
Intent chooserIntent = Intent.createChooser(target, "Choose");
// Add all other intents
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));
this.startActivityForResult(
chooserIntent
, CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE);
和方法CropImage.getGalleryIntents:
public static List<Intent> getGalleryIntents(
@NonNull PackageManager packageManager, String action, boolean includeDocuments) {
List<Intent> intents = new ArrayList<>();
Intent galleryIntent =
action == Intent.ACTION_GET_CONTENT
? new Intent(action)
: new Intent(action, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
galleryIntent.setType("image/*");
List<ResolveInfo> listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
for (ResolveInfo res : listGallery) {
Intent intent = new Intent(galleryIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(res.activityInfo.packageName);
intents.add(intent);
}
// remove documents intent
if (!includeDocuments) {
for (Intent intent : intents) {
if (intent
.getComponent()
.getClassName()
.equals("com.android.documentsui.DocumentsActivity")) {
intents.remove(intent);
break;
}
}
}
return intents;
}
我有正确的解决方案或解决方法吗?谢谢。
为您的 galleryIntent
使用 ACTION_OPEN_DOCUMENT
。
但要注意即使是ACTION_OPEN_DOCUMENT
也会在几个小时后失效
无限制访问图像的唯一方法是将其保存到应用程序自己的存储空间并在那里访问以供以后使用。
我正在使用照片裁剪库(此处为 2.7 版:https://github.com/ArthurHub/Android-Image-Cropper)使用图库应用程序(如三星图库、google 照片、文件等)挑选和裁剪图像。 该库通过使用 Intent.ACTION_GET_CONTENT 启动选择器意图来工作,并且在用户选择图像后,Uri 被传递到裁剪视图,从而打开位图。 在某些情况下,我需要通过自动使用用户最后选择的照片中的 Uri 来重置裁剪。因此,在用户选择图像后,我记住 Uri,然后使用相同的 Uri 开始裁剪步骤(解码位图)。
这适用于 Samsung Gallery,但我对 Google 照片有问题,当重新打开 uri 时会抛出 SecurityException。因此,只有在用户第一次选择图像时,使用 google 张照片的图像选择才能正常工作。当我尝试传递相同的 Uri 时(当我重置作物时),我得到下面的异常(这也发生在 "Files" - 我在模拟器和摩托罗拉上测试过):
java.lang.RuntimeException: Failed to load sampled bitmap: content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253Ace62f6ca-6054-486b-bbb1-b6145ac50272/ORIGINAL/NONE/27402515
Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at com.theartofdev.edmodo.cropper.BitmapUtils.decodeSampledBitmap(BitmapUtils.java:130)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:73)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:24)
at android.os.AsyncTask.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at android.os.Parcel.createException(Parcel.java:1950)
at android.os.Parcel.readException(Parcel.java:1918)
at android.os.Parcel.readException(Parcel.java:1868)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:4118)
用于向用户显示选择照片的应用程序的代码是:
List<Intent> allIntents = new ArrayList<>();
PackageManager packageManager = this.getPackageManager();
boolean includeCamera = useCamera;
// collect all camera intents if Camera permission is available
if (!CropImage.isExplicitCameraPermissionRequired(this) && includeCamera) {
allIntents.addAll(CropImage.getCameraIntents(this, packageManager));
}
boolean includeDocuments = false;
if (useGallery) {
List<Intent> galleryIntents =
CropImage.getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments);
if (galleryIntents.size() == 0) {
// if no intents found for get-content try pick intent action (Huawei P9).
galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_PICK, includeDocuments);
}
allIntents.addAll(galleryIntents);
}
Intent target;
if (allIntents.isEmpty()) {
target = new Intent();
} else {
target = allIntents.get(allIntents.size() - 1);
allIntents.remove(allIntents.size() - 1);
}
// Create a chooser from the main intent
Intent chooserIntent = Intent.createChooser(target, "Choose");
// Add all other intents
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));
this.startActivityForResult(
chooserIntent
, CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE);
和方法CropImage.getGalleryIntents:
public static List<Intent> getGalleryIntents(
@NonNull PackageManager packageManager, String action, boolean includeDocuments) {
List<Intent> intents = new ArrayList<>();
Intent galleryIntent =
action == Intent.ACTION_GET_CONTENT
? new Intent(action)
: new Intent(action, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
galleryIntent.setType("image/*");
List<ResolveInfo> listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
for (ResolveInfo res : listGallery) {
Intent intent = new Intent(galleryIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(res.activityInfo.packageName);
intents.add(intent);
}
// remove documents intent
if (!includeDocuments) {
for (Intent intent : intents) {
if (intent
.getComponent()
.getClassName()
.equals("com.android.documentsui.DocumentsActivity")) {
intents.remove(intent);
break;
}
}
}
return intents;
}
我有正确的解决方案或解决方法吗?谢谢。
为您的 galleryIntent
使用 ACTION_OPEN_DOCUMENT
。
但要注意即使是ACTION_OPEN_DOCUMENT
也会在几个小时后失效
无限制访问图像的唯一方法是将其保存到应用程序自己的存储空间并在那里访问以供以后使用。