1. 程式人生 > >正確將圖片儲存到相簿的方法

正確將圖片儲存到相簿的方法

而如果這個檔案變成了一張圖片,那你涉及到的就不僅僅是一個 I/O 操作了,還需要考慮如何更新 MediaStore,這樣才可以在系統相簿中。

刷新系統 Media 通常有如下幾種方式:

  • 通過操作 MediaStore 類。
  • 傳送廣播更新 MediaStore。
  • 通過操作 MediaScannerConnection 類。

操作 MediaStore

MediaStore.Images.Media它提供了幾個 inserImage () 方法,供我們向 MediaStore 中插入圖片資料,併產生一個縮圖。

  /**
             * Insert an image and create a thumbnail for it.
             *
             * @param
cr The content resolver to use * @param source The stream to use for the image * @param title The name of the image * @param description The description of the image * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored * for any reason. */
public static final String insertImage(ContentResolver cr, Bitmap source, String title, String description) { ContentValues values = new ContentValues(); values.put(Images.Media.TITLE, title); values.put(Images.Media.DESCRIPTION, description); values.put(Images.Media.MIME_TYPE, "image/jpeg"
); Uri url = null; String stringUrl = null; /* value to be returned */ try { url = cr.insert(EXTERNAL_CONTENT_URI, values); if (source != null) { OutputStream imageOut = cr.openOutputStream(url); try { source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut); } finally { imageOut.close(); } long id = ContentUris.parseId(url); // Wait until MINI_KIND thumbnail is generated. Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null); // This is for backward compatibility. Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F, Images.Thumbnails.MICRO_KIND); } else { Log.e(TAG, "Failed to create thumbnail, removing original"); cr.delete(url, null, null); url = null; } } catch (Exception e) { Log.e(TAG, "Failed to insert image", e); if (url != null) { cr.delete(url, null, null); url = null; } } if (url != null) { stringUrl = url.toString(); } return stringUrl; }

這個方法傳遞進去的是一個 Bitmap 物件,其餘的 title 和 description 分別是圖片檔案的名稱和一段描述。

廣播可以更新 MediaStore

說到廣播,在 Android 4.4 之前,是可以通過 ACTION_MEDIA_MOUNTED 廣播,來通知系統重新整理 MediaStore 的,不過假如你現在還在依賴這條廣播,你會得到一個錯誤資訊。

E/AndroidRuntime(23718): java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED from pid=23718, uid=10097

在 Android 4.4 之後,這個廣播只能由系統進行廣播,App 只能對該廣播進行監聽,在當前的系統分佈環境下,這條路已經走不通了。
這樣設計也很好理解,畢竟掃描全盤是非常的耗資源,所以系統肯定要把全盤掃描的許可權拿在自己手裡不開放出來,避免被第三方 App 濫用。
不過 Android 依然給我們提供了替代方案,那就是用 MediaScannerConnection 或者傳送 ACTION_MEDIA_SCANNER_SCAN_FILE 廣播。
接下來就來說說 ACTION_MEDIA_SCANNER_SCAN_FILE 這個廣播。
通過廣播重新整理 MediaStore 的方式非常的簡單,只需要指定檔案路徑和 Action 就好了。

val saveAs = "Your_Created_Image_File_Path"
val contentUri = Uri.fromFile(File(saveAs))
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,contentUri)
sendBroadcast(mediaScanIntent)

正常情況下,它是沒有問題的,不過假如你發現它不生效,就需要檢查一下你檔案的路徑是否傳遞正確。
通過檢視 MediaScannerReceiver 的原始碼,可以發現 onReceive() 方法中,針對 ACTION_MEDIA_SCANNER_SCAN_FILE 還有一個限制條件,那就是傳遞進去的檔案絕對路徑,必須是以 Environment.getExternalStorageDirectory() 方法的返回值開頭。

操作 MediaScannerConnection 類

重新整理 MediaStore 還有一個最通用也是我推薦的一個方法,那就是使用 MediaScannerConnection 進行操作。

不同於 MediaStore.Image.Media 和廣播的方式,使用 MediaScannerConnection 不僅可以儲存檔案,還可以指定檔案路徑,最好的就是,它還支援重新整理完成的回撥。

如果我們對時序有要求,並且需要制定檔案儲存路徑的話,最好的方式就是直接使用 MediaScannerConnection 類進行操作,並且這也應該是相容最好的方式。

這裡我們主要是利用 MediaScannerConnection 類的 scanFile() 方法進行觸發掃描。
通過 scanFile() 方法,我們只需要制定一個待重新整理的檔案路徑和對應的 MimeType 即可,它支援傳遞多個路徑,也可就是支援批量掃描。

注意這裡的 MimeType 是一定要填寫的,並且不能寫萬用字元 / 或 null,否則會導致重新整理失敗,通常我們儲存的是一個圖片的話,只需要傳遞 image/jpeg 即可。

最後一個引數, onScanCompletedListener 中可以監聽我們掃描的結果,需要注意的是,假如這裡掃描的是多個檔案路徑,它也會被回撥多次。所以如果有什麼在重新整理之後的後續操作,就需要特殊處理一下(原因後面是說)。

MediaScannerConnection.scanFile(this
        , arrayOf(picFile.absolutePath)
        , arrayOf("image/jpeg"), { path, uri ->
    Log.i("cxmyDev", "onScanCompleted : " + path)
})