获得权限拒绝例外

mar*_*595 24 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);
Run Code Online (Sandbox Code Playgroud)

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

content://com.android.providers.media.documents/document/image%3A42555
Run Code Online (Sandbox Code Playgroud)

然后,如果文件是图像,我正在解码它:

InputStream streamForDecodeBitmap = MyApp.getContext().getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(streamForDecodeBitmap, null, options);
Run Code Online (Sandbox Code Playgroud)

当用户单击按钮时,我通过意图将Uris列表传递给另一个活动,在此活动中,在AsyncTask中,我在base64中编码该文件以通过网络发送它:

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

问题是当我打开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
Run Code Online (Sandbox Code Playgroud)

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

<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"/>
Run Code Online (Sandbox Code Playgroud)

我正在使用KITKAT(API 19)测试设备.

Par*_*rth 35

请检查以下问题:
Android KitKat securityException尝试从MediaStore读取
权限拒绝:打开提供程序

对于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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

参考: KitKat上的Android Gallery为Intent.ACTION_GET_CONTENT返回不同的Uri http://developer.android.com/guide/topics/providers/document-provider.html#client


小智 20

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

protected void onActivityResult(int reqCode, int resultCode, Intent data)
Run Code Online (Sandbox Code Playgroud)

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

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

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

一旦有了编码字符串,就可以将它传递给可能需要它的任何其他活动,或者你可以将它传递给AsyncTask以便发送到其他地方.


Alé*_*lho 10

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

activity.getContentResolver()  
Run Code Online (Sandbox Code Playgroud)

代替 MyApp.getContext().getContentResolver()

对我来说显然是这样,它不需要添加android.permission.MANAGE_DOCUMENTS到清单.

并且您使用以下方法调用intent:

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);
}
Run Code Online (Sandbox Code Playgroud)

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


dhe*_*sse 5

Writing ACTION_OPEN_DOCUMENT is not helping entirely. After a restart your URI Permission is gone even with this action.

   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);
        }
Run Code Online (Sandbox Code Playgroud)

To complete Alécio Carvalho's answer look at this documentation on the same page. There is a good explanation regarding URI Permissions and how to make them persist.

Just add this to your onActivityResult method and your permission persists through a device restart. (Tested with a 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())
  }
}
Run Code Online (Sandbox Code Playgroud)

(sorry kotlin lover...java version is inside the link)