2014-03-04 63 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) 
}