隨着各大廠商對android11的升級推送,現在已經有了一定android11的機型,關於android11的適配網上有很多相關的文章
這里主要強調下android11的分區存儲一般情況下遇到的問題。
一般非垃圾清理類app或者沒有特殊需求的app,主要在調用圖片裁剪會遇到android11的問題。
如小米10:

小米10裁剪.jpg

小米10裁剪報錯.jpg
小米10報錯:保存時發生錯誤,保存失敗
糾其原因就是android11在更新后,會強制使用分區存儲:
在tagSdk<30(29未忽略分區存儲情況下requestLegacyExternalStorage=true)其他應用,無法訪問app私有目錄下的文件;所以導致了上圖出現系統裁剪應用,無法訪問app下的裁剪副本,這里就講到了系統裁剪功能流程:
1:在拍照或者相冊中拿到圖片的uri
2:創建圖片intent傳入
ImageType:intent.setDataAndType(uri, "image/*")
3:創建裁剪輸出路徑:
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://"+ mOnputFile.getAbsolutePath()))
;
4:裁剪完成后的圖片文件就是mOnputFile
這里在android11上出錯就是在第3步驟上,內置系統裁剪應用無法訪問自己的app私有目錄下的圖片(這里大多數應用在訪問圖片文件的時候應該都是采用fileprovider創建的私有目錄的uri吧)
我們應用的文件存儲的內置路徑是:
storage/emulated/0/Android/data/com.xx.xxxx/cache
可以看到
com.xx.xxxx
就是內置的私有目錄
所以,在android11上,通過fileprovider創建的uri path只要改為公域,系統裁剪應用就可以訪問裁剪過后,公域圖片地址:
storage/emulated/0/Pictures
可以看到沒有包名。
獲取公域地址方法:
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath()
完整代碼,這里以相冊為例:
// 啟動相冊 public void openAlbum() { Intent intent_album = new Intent("android.intent.action.GET_CONTENT"); intent_album.setType("image/*"); startActivityForResult(intent_album, ICON_FROM_ALBUM); }
啟動相冊后,在onActivityResult里獲取uri
case ICON_FROM_ALBUM: //從相冊選擇 if (data == null || data.getData() == null) { return; } clipPhoto(Uri.fromFile(new File(ImageFileUtils.getPath(this, data.getData())) )); //裁剪圖片 break;
圖片裁剪,這里在tagSdk低於30的情況下做判斷,注意7.0的通過provider的uri來訪問媒體
//裁剪圖片 private void clipPhoto(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", new File(uri.getPath())); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } intent.setDataAndType(uri, "image/*"); // 下面這個crop=true是設置在開啟的Intent中設置顯示的VIEW可裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是寬高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪圖片寬高 intent.putExtra("outputX", 150); intent.putExtra("outputY", 150); intent.putExtra("circleCrop", true); if (Build.VERSION.SDK_INT >= 30) { //android 11以上,將文件創建在公有目錄 String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath(); //storage/emulated/0/Pictures mOnputFile = new File(path, System.currentTimeMillis() + ".png"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + mOnputFile.getAbsolutePath())); } else { //storage/emulated/0/Android/data/com.xxxxx/cache mOnputFile = new File(sdPath, System.currentTimeMillis() + ".png"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + mOnputFile.getAbsolutePath())); } startActivityForResult(intent, ICON_CROP); }
依然在onActivityResult中處理
case ICON_CROP: //mOnputFile就是裁剪副本,這里簡單地判斷下文件,可以處理裁剪圖片的顯示,或者上傳 break;

成功.jpg
總結:app在Android11系統上強制使用分區存儲,私有目錄下的文件無法被訪問,如果有大量文件的話,要做數據遷移(就是將需要共享的文件從上述的私有目錄移到共有目錄下),這里網上有很多相關的文章。
題外話:還有如果用到umeng分享的話,在android11的設備上分享圖片也會有問題類似的問題,具體可以更新新的umeng shareSdk,友盟已經做了相關適配。