获得权限拒绝例外

时间:2014-03-04 16:52:34

标签: android

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

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);

当用户点击一个按钮时,我通过意图将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)测试设备。

6 个答案:

答案 0 :(得分:35)

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

对于KitKat上的同样问题,我使用了它。这是我从其中一个Stack Overflow链接找到的选项/解决方法,您可以从Downloads / Recent中选择文件。

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

答案 1 :(得分:20)

当控件和数据返回到

中的活动时
protected void onActivityResult(int reqCode, int resultCode, Intent data)

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

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

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

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

答案 2 :(得分:10)

确保您使用请求内容的活动中的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

答案 3 :(得分:3)

写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版本在链接里面)

答案 4 :(得分:0)

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

  

Android设置 - &gt;应用 - &gt;应用名称 - &gt;存储 - &gt;清除访问

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

答案 5 :(得分:0)

在kotlin中通过在Intent

之后放置运算符"?" = (nullable)来解决

如下代码:

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