【问题标题】:Getting Permission Denial Exception获取权限拒绝异常
【发布时间】:2014-04-06 08:20:17
【问题描述】:

我的应用中有一个活动,允许用户从设备中一一选择多个文件,我正在使用这样的意图:

Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_attachments_activity_chooser_label)), SELECT_PICTURE);

这工作得很好,我正在选择文件的 Uri,它们看起来像这样:

content://com.android.providers.media.documents/document/image%3A42555

然后,如果文件是图像,我将使用以下方法对其进行解码:

InputStream streamForDecodeBitmap = MyApp.getContext().getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(streamForDecodeBitmap, null, options);

当用户点击一个按钮时,我通过 Intent 将 Uris 列表传递给另一个活动,在这个活动中,在一个 AsyncTask 中,我将文件编码为 base64 以通过网络发送:

InputStream is = MyApp.getContext().getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(is);
is.close();
return Base64.encodeToString(inputData, Base64.DEFAULT);

问题是当我打开 inputStream 时,有时它可以工作,但大多数时候我都会收到此异常:

E/AndroidRuntime(22270): Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{42858fe0 22270:co.uk.manifesto.freeagentapp/u0a246} (pid=22270, uid=10246) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS

这些是我清单中的所有权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>

我正在使用 KITKAT (API 19) 的设备中进行测试。

【问题讨论】:

    标签: android


    【解决方案1】:

    请也检查这些问题:
    Android KitKat securityException when trying to read from MediaStore
    Permission denial: opening provider

    对于 KitKat 上的相同问题,我使用了这个。这是我从 Stack Overflow 链接之一中找到的一个选项/解决方法,您将能够从“下载/最近”中选择文件。

    public static final int KITKAT_VALUE = 1002;
    
    Intent intent;
    
    if (Build.VERSION.SDK_INT < 19) {
        intent = new Intent();
        intent.setAction(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        startActivityForResult(intent, KITKAT_VALUE);
    } else {
        intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        startActivityForResult(intent, KITKAT_VALUE);
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == KITKAT_VALUE ) {
            if (resultCode == Activity.RESULT_OK) {
                // do something here
            }
        }
    }
    

    参考: Android Gallery on KitKat returns different Uri for Intent.ACTION_GET_CONTENT http://developer.android.com/guide/topics/providers/document-provider.html#client

    【讨论】:

      【解决方案2】:

      当控制和数据返回到您的活动时

      protected void onActivityResult(int reqCode, int resultCode, Intent data)
      

      同时,您的活动被授予访问传递的意图中的图像 url 的权限。您的活动将能够读取意图中的 url 并显示图像。但是您将无法将任何权限传递给其他活动或新的执行线程。这就是为什么您会收到 android.permission.MANAGE_DOCUMENTS 的 SecurityException。

      避免权限问题的一种方法是获取具有权限的活动中的数据。在您的情况下,从具有权限的活动中进行编码

      InputStream is = getContentResolver().openInputStream(uri);
      byte[] inputData = getBytes(is);
      is.close();
      String encoded = Base64.encodeToString(inputData, Base64.DEFAULT);
      

      获得编码字符串后,您可以将其传递给可能需要它的任何其他活动,或者您可以将其传递给 AsyncTask 以发送到其他地方。

      【讨论】:

      • 很好的答案,我已经重构了我的代码,但尚未在引发此问题的设备上进行测试。不过,这是有道理的,在我的情况下,我正在使用 ACTION_NEW_PICTURE 和 ACTION_NEW_VIDEO 在 BroadcastReceiver 中收听新照片/视频。我已经在 UI 线程上构建媒体查询,然后将光标传递给后台任务进行处理。
      • 有没有官方文档说权限没有被传递给“新的执行线程”?
      【解决方案3】:

      确保您使用的是请求内容的 Activity 中的 ContentResolver,例如:

      activity.getContentResolver()  
      

      而不是MyApp.getContext().getContentResolver()

      这显然是我的情况,它不需要将android.permission.MANAGE_DOCUMENTS 添加到清单中。

      你使用这个来调用意图:

      if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
          intent.setAction(Intent.ACTION_GET_CONTENT);
      } else {
          intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
          intent.addCategory(Intent.CATEGORY_OPENABLE);
      }
      

      更多信息:https://developer.android.com/guide/topics/providers/document-provider.html#client

      【讨论】:

        【解决方案4】:

        编写 ACTION_OPEN_DOCUMENT 并没有完全帮助。重新启动后,即使执行此操作,您的 URI 权限也会消失。

           if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                    intent.setAction(Intent.ACTION_GET_CONTENT);
                } else {
                    intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                }
        

        要完成Alécio Carvalho's 答案,请查看this documentation on 同一页面。关于 URI 权限以及如何使它们持久存在,有一个很好的解释。

        只需将此添加到您的 onActivityResult 方法中,您的权限就会在设备重启后持续存在。 (使用 Google Pixel 进行测试。)

        override fun onActivityResult(requestCode:Int, resultCode:Int, intent:Intent?) {
        if(requestCode == SELECT_PICTURE && intent != null){
            val uri = intent.getData()
            val takeFlags = intent.getFlags() and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            activity.getContentResolver().takePersistableUriPermission(uri, takeFlags)
            PreferenceHelper.setHeaderImageUri(activity, uri.toString())
          }
        }
        

        (对不起kotlin情人...java版本在链接内)

        【讨论】:

          【解决方案5】:

          值得注意的是,如果用户手动删除了您之前保存的 Uri 的权限,那么您将获得相同的异常。

          Android 设置 -> 应用 -> 应用名称 -> 存储 -> 清除访问权限

          您需要在使用 Uri 时检查您是否仍然可以访问它。

          【讨论】:

          • 在需要时如何再次获得访问权限?
          • 您需要再次向用户请求权限。
          【解决方案6】:

          在 kotlin 中通过将运算符 "?" = (nullable) 放在 Intent 之后来解决

          如下代码:

          override func onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 
              super.onActivityResult(requestCode, resultCode, data)
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-27
            • 2012-03-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多