无法在Android 11上以编程方式拍照 - 意图返回已取消状态

4

启动一个意图:

  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  CurrentFile = new File(getTempFileString());
  CurrentUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", CurrentFile);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, CurrentUri);
  intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
  startActivityForResult(intent, IMAGE_CAPTURE);

曾经可以工作,现在不行了。我使用的方法是在意图之后启动:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
}

resultCode曾经使用RESULT_OK,但现在已更改为RESULT_CANCELED

如果我停止检查resultCode并继续运行,我发现照片不存在。

根据CommonsWare的评论,我提取了日志。在此操作期间生成的654行日志中,有四行似乎与此相关。

2020-10-12 11:03:04.301 1471-1763/? E/MediaProvider: Creating a non-default top level directory or deleting an existing one is not allowed!
2020-10-12 11:03:04.310 477-2112/? I/AppsFilter: interaction: PackageSetting{240e1c6 com.[my app package]/10151} -> PackageSetting{193734c com.android.camera2/10124} BLOCKED
2020-10-12 11:03:04.553 390-9884/? W/ServiceManager: Permission failure: android.permission.SYSTEM_CAMERA from uid=10124 pid=11746
2020-10-12 11:03:14.097 11746-11746/? E/CAM_StateSavePic: exception while saving result to URI: Optional.of(content://[my app package].provider/external_files/[The SD card path I asked for]/1602518584301.jpg)

我要求将文件保存在这里:

new File(Environment.getExternalStorageDirectory(), getString(a.getApplicationInfo().labelRes))

看起来这似乎是个问题。


1
检查Logcat以查看相机应用程序是否记录了与您调用此“Intent”相关的任何消息。该应用程序恰好是您的默认应用程序。 - CommonsWare
谢谢,这确实有所帮助。我更新了问题,现在有更多的线索了。 - Paul
3
明年当您的应用程序的 targetSdkVersion 达到 30 后,您将无法写入外部存储的根目录。我强烈建议您将此图像写入其他位置--例如,您的应用程序应该能够贡献给 Pictures/。如果您非常需要在短期内在此处编写,请在清单文件的 <application> 元素中添加 android:requestLegacyExternalStorage="true" - CommonsWare
哇,我欠你一瓶啤酒。我永远也找不到这个问题。谢谢! - Paul
3个回答

12

实际上,这里应该归功于@CommonsWare。问题出在源代码中的getTempFileString()调用上。我试图将文件保存到临时路径,以便可以立即删除它。文件仅存在短时间。Google阻止了对此的访问。 Camera 应用程序收到了有关无法保存到此路径的错误,但它没有抛出错误或显示消息。它只向我的应用程序返回了一个RESULT_CANCELED的结果。我通过更新所使用的路径为本地缓存目录getExternalCacheDir()并在其中使用子目录来解决了这个问题。

编辑: 添加了对我有效的代码。为了使其简洁明了,已删除了特定于应用程序的文本和错误处理。

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

File path = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "My Application Directory");

if (!path.exists()) {
   path.mkdir();
}

File imageFile = File.createTempFile("Your file Name", ".jpg", path);

Context context = getActivity().getBaseContext();
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", imageFile);

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
   String packageName = resolveInfo.activityInfo.packageName;
   context.grantUriPermission(packageName, CurrentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

startActivityForResult(intent, IMAGE_CAPTURE); // IMAGE_CAPTURE = 0

3
我试图将文件保存到临时路径,以便保存后立即删除它。你可以考虑使用 getCacheDir() 方法,FileProvider 支持该方法并且不会有权限问题。无论如何,很高兴看到你已经解决了问题! - CommonsWare
你成功使用了getExternalCacheDir()吗?我从相机意图中得到了一个结果代码为0,当我查看logcat时,我看到了一个访问被拒绝的错误(在Andriod 11模拟器上)。 - Rob
1
@Rob 我更新了我的回答,并附上了一些源代码。 - Paul
我遇到了一些问题,所以我回到了Android文档,现在似乎很高兴使用getExternalFilesDir,只要我按照https://developer.android.com/training/camera/photobasics#java中给出的所有步骤去做。 - Rob

1

你不应该使用 "getExternalStoragePublicDirectory()",因为这是共享存储行为,并且从(可能是)2021年7月5日起,Play Store 上的所有应用程序都必须针对 Android SDK 30,并仅使用 Scoped Storage(因此您无法使用 "requestLegacyStorage",我不确定 "preserveLegacyStorage" 是否可以)。

我建议您将目标路径更改为:

context.getExternalFilesDir(null).getPath();

祝您有一个愉快的一天和愉快的编程 :D

0

这是一种奇怪的行为,但如果你使用 MediaStore.EXTRA_OUTPUT 标志方法,则 onActivityResult() 将始终返回空数据,并且您需要在此指定的位置查找数据:

CurrentUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", CurrentFile);

所以有两种不同的处理你正在寻找的结果的方式


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接