1. 程式人生 > >android 4.4 5.0不能獲取到圖片路徑問題詳解

android 4.4 5.0不能獲取到圖片路徑問題詳解

   最近在做一個從相簿選擇圖片或拍照,然後裁剪的功能.本來是沒問題的,一直在用

  1. Intent intent=new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  
的方式來做,是呼叫系統圖庫來做,但是發現如果有圖片是同步到google相簿的話,相簿裡面能看到一個auto backup的目錄,點進去選圖片的話是無法獲取到圖片的路徑的.因為那些圖片根本就不存在於手機上.然後看到無論是百度貼吧,Instagram,或者還有些會選取圖片做修改的app,都是用一個很漂亮的圖片選擇器(4.4以上,4.3的還是用系統舊的相簿).


而這個圖片選擇器可以遮蔽掉那個auto backup的目錄.所以就開始打算用這個圖片選擇器來選圖片了.
這個方法就是

  1. Intent intent=new Intent(Intent.ACTION_GET_CONTENT);//ACTION_OPEN_DOCUMENT
  2. intent.addCategory(Intent.CATEGORY_OPENABLE);  
  3. intent.setType("image/jpeg");  
  4. if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT){                  
  5.         startActivityForResult(intent, SELECT_PIC_KITKAT);    
  6. }else{                
  7.         startActivityForResult(intent, SELECT_PIC);   
  8. }   

為什麼要分開不同版本呢?其實在4.3或以下可以直接用ACTION_GET_CONTENT的,在4.4或以上,官方建議用ACTION_OPEN_DOCUMENT,但其實都不算太大區別,區別是他們返回的Uri,那個才叫大區別.這就是困擾了我一整天的問題所在了.

4.3或以下,選了圖片之後,根據Uri來做處理,很多帖子都有了,我就不詳細說了.主要是4.4,如果使用上面pick的原生方法來選圖,返回的uri還是正常的,但如果用ACTION_GET_CONTENT的方法,返回的uri跟4.3是完全不一樣的,4.3返回的是帶檔案路徑的,而4.4返回的卻是content://com.android.providers.media.documents/document/image:3951這樣的,沒有路徑,只有圖片編號的uri.這就導致接下來無法根據圖片路徑來裁剪的步驟了.

還好找了很多方法,包括加許可權啊什麼的,中間還試過用一些方法,自己的app沒崩潰,倒是讓系統圖庫崩潰了,引發了java.lang.SecurityException.

  1. Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{437b5d88 9494:com.google.android.gallery3d/u0a20} (pid=9494, uid=10020) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS  

看來4.4的系統還是有些bug.重點來了,4.4得到的uri,需要以下方法來獲取檔案的路徑
  1. publicstatic String getPath(final Context context, final Uri uri) {  
  2.     finalboolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;  
  3.     // DocumentProvider
  4.     if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {  
  5.         // ExternalStorageProvider
  6.         if (isExternalStorageDocument(uri)) {  
  7.             final String docId = DocumentsContract.getDocumentId(uri);  
  8.             final String[] split = docId.split(":");  
  9.             final String type = split[0];  
  10.             if ("primary".equalsIgnoreCase(type)) {  
  11.                 return Environment.getExternalStorageDirectory() + "/" + split[1];  
  12.             }  
  13.             // TODO handle non-primary volumes
  14.         }  
  15.         // DownloadsProvider
  16.         elseif (isDownloadsDocument(uri)) {  
  17.             final String id = DocumentsContract.getDocumentId(uri);  
  18.             final Uri contentUri = ContentUris.withAppendedId(  
  19.                     Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));  
  20.             return getDataColumn(context, contentUri, nullnull);  
  21.         }  
  22.         // MediaProvider
  23.         elseif (isMediaDocument(uri)) {  
  24.             final String docId = DocumentsContract.getDocumentId(uri);  
  25.             final String[] split = docId.split(":");  
  26.             final String type = split[0];  
  27.             Uri contentUri = null;  
  28.             if ("image".equals(type)) {  
  29.                 contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
  30.             } elseif ("video".equals(type)) {  
  31.                 contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;  
  32.             } elseif ("audio".equals(type)) {  
  33.                 contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;  
  34.             }  
  35.             final String selection = "_id=?";  
  36.             final String[] selectionArgs = new String[] {  
  37.                     split[1]  
  38.             };  
  39.             return getDataColumn(context, contentUri, selection, selectionArgs);  
  40.         }  
  41.     }  
  42.     // MediaStore (and general)
  43.     elseif ("content".equalsIgnoreCase(uri.getScheme())) {  
  44.         // Return the remote address
  45.         if (isGooglePhotosUri(uri))  
  46.             return uri.getLastPathSegment();  
  47.         return getDataColumn(context, uri, nullnull);  
  48.     }  
  49.     // File
  50.     elseif ("file".equalsIgnoreCase(uri.getScheme())) {  
  51.         return uri.getPath();  
  52.     }  
  53.     returnnull;  
  54. }  
  55. /** 
  56.  * Get the value of the data column for this Uri. This is useful for 
  57.  * MediaStore Uris, and other file-based ContentProviders. 
  58.  * 
  59.  * @param context The context. 
  60.  * @param uri The Uri to query. 
  61.  * @param selection (Optional) Filter used in the query. 
  62.  * @param selectionArgs (Optional) Selection arguments used in the query. 
  63.  * @return The value of the _data column, which is typically a file path. 
  64.  */
  65. publicstatic String getDataColumn(Context context, Uri uri, String selection,  
  66.         String[] selectionArgs) {  
  67.     Cursor cursor = null;  
  68.     final String column = "_data";  
  69.     final String[] projection = {  
  70.             column  
  71.     };  
  72.     try {  
  73.         cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,  
  74.                 null);  
  75.         if (cursor != null && cursor.moveToFirst()) {  
  76.             finalint index = cursor.getColumnIndexOrThrow(column);  
  77.             return cursor.getString(index);  
  78.         }  
  79.     } finally {  
  80.         if (cursor != null)  
  81.             cursor.close();  
  82.     }  
  83.     returnnull;  
  84. }  
  85. /** 
  86.  * @param uri The Uri to check. 
  87.  * @return Whether the Uri authority is ExternalStorageProvider. 
  88.  */
  89. publicstaticboolean isExternalStorageDocument(Uri uri) {  
  90.     return"com.android.externalstorage.documents".equals(uri.getAuthority());  
  91. }  
  92. /** 
  93.  * @param uri The Uri to check. 
  94.  * @return Whether the Uri authority is DownloadsProvider. 
  95.  */
  96. publicstaticboolean isDownloadsDocument(Uri uri) {  
  97.     return"com.android.providers.downloads.documents".equals(uri.getAuthority());  
  98. }  
  99. /** 
  100.  * @param uri The Uri to check. 
  101.  * @return Whether the Uri authority is MediaProvider. 
  102.  */
  103. publicstaticboolean isMediaDocument(Uri uri) {  
  104.     return"com.android.providers.media.documents".equals(uri.getAuthority());  
  105. }  
  106. /** 
  107.  * @param uri The Uri to check. 
  108.  * @return Whether the Uri authority is Google Photos. 
  109.  */
  110. publicstaticboolean isGooglePhotosUri(Uri uri) {  
  111.     return"com.google.android.apps.photos.content".equals(uri.getAuthority());  
  112. }  

這樣,就可以在4.4上用漂亮的圖片選擇器,選到我們想要的檔案,又不會出問題了.

昨天發現了個bug,如果在4.4上面不用"圖片"來選,用"相簿"來選,就會無法讀取到圖片路徑,所以只需要加個判斷,如果是用舊方式來選,就用舊方式來讀,就是如果
DocumentsContract.isDocumentUri(context, uri) 返回false的話,就用舊的方式

  1. publicstatic String selectImage(Context context,Intent data){  
  2.         Uri selectedImage = data.getData();  
  3. //      Log.e(TAG, selectedImage.toString());
  4.         if(selectedImage!=null){              
  5.             String uriS