2014-03-04 38 views
18

我有我的应用程序的活动,可以让用户通过一个从设备一个选择几个文件,我使用像这样的意图:得到允许拒绝例外

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的时候,看起来就像这样:

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

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

然后,如果该文件是一个形象,我与它的解码

当用户点击一个按钮时,我通过意图和本次活动通过URI列表到另一活动,在的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)的设备中测试。

回答

29

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

有关奇巧同样的问题,我用这个。这是我从堆栈溢出链接中找到的选项/解决方法,您可以从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

15

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

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

您的活动,同时,被赋予的权限来访问图像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发送别处。

+0

伟大的答案,我已经重构我的代码,但没有抛出这个问题在设备上测试。这是有道理的,但在我的情况下,我正在使用ACTION_NEW_PICTURE和ACTION_NEW_VIDEO在BroadcastReceiver中侦听新照片/视频。我已经移动在UI线程上构建媒体查询,然后将光标传递给后台任务进行处理。 – Meanman

+0

是否有任何官方文档说明未将权限传递给“新执行线程”? –

8

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

2

写作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方法中,并且通过设备重新启动您的权限仍然存在。 (测试用谷歌像素)

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()) 
    } 
} 

(对不起科特林情人... Java版本是内部链接)

0

值得注意的是,如果用户手动删除权限一个URI,您以前保存,那么你会得到这个相同的例外。

Android设置 - >应用程序 - >应用程序名称 - >存储 - >清除访问

你需要检查你仍然可以访问开放的,当你使用它。

0

在科特林通过将运营商"?" = (nullable)意向后

如下面的代码解决:

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