Android保存照片到相冊

Android保存照片到相冊

前言

隨着Android系統的不斷升級 從最初的第一個版本 更新到如今 Android 11.0 Beta都出爐了 Android 11.0也即將面試 系統的不斷更新完善 用戶體驗也是蹭蹭蹭的 隱私安全方面也是愈來愈給力了 這對用戶固然是一級棒 對於開發者 簡直無力吐槽 由於碎片化問題 加上版本更新迭代 廢棄淘汰一堆 API 腦瓜子疼。android

正文

這幾天剛剛須要保存視頻到相冊 發現 我去 之前的方法好像不太給力了,磨了 我很久。。由於我手機是Android 10 版本 從用戶隱私增強了 最大的變化就是 存儲權限web

Android 10 在外部存儲設備中爲每一個應用提供了一個「隔離存儲沙盒」(例如 /sdcard)。任何其餘應用都沒法直接訪問您應用的沙盒文件。因爲文件是您應用的私有文件,所以您再也不須要任何權限便可在外部存儲設備中訪問和保存本身的文件。此變動可以讓您更輕鬆地保證用戶文件的隱私性,並有助於減小應用所需的權限數量。面試

因此就是說咱們不能直接訪問 根目錄 了? 其實 在Android 10 上還不是徹底杜絕你使用的 你仍是能夠兼容低版本 那怎麼作呢安全

<application  ...  android:requestLegacyExternalStorage="true"> </application> 複製代碼

只須要在 你的AndroidManifest.xml 文件中 加入這行代碼 意思就是申請舊版本的外部存儲 那麼你仍是可與愉快玩耍的app

但是舊版本的外部存儲權限都已經廢棄了 這樣作是能夠解決當前的問題 可是在Android 11 上 講嚴格執行沙盒存儲方式 也就是說 這樣的代碼在Android 11 上已經沒法兼容了 而且的 10 的系統兼容也不夠編輯器

擼碼環節

那麼開始擼代碼把ui

/**  *保存bitmap  */ fun saveBitmap2Gallery(context: Context, bitmap: Bitmap): Boolean {  val name=System.currentTimeMillis().toString()  val photoPath=Environment.DIRECTORY_DCIM + "/Camera"  val contentValues = ContentValues().apply {  put(MediaStore.MediaColumns.DISPLAY_NAME,name )  put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")  put(MediaStore.MediaColumns.RELATIVE_PATH, photoPath)//保存路徑  put(MediaStore.MediaColumns.IS_PENDING, true)  }  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  //返回出一個URI  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  contentValues  ) ?: return false //爲空的話 直接失敗返回了   //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use {  it ?: return false  bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)  }  return true  } else {  MediaStore.Images.Media.insertImage(context.contentResolver, bitmap, "title", "desc")  return true  }  } 複製代碼

就上面的代碼 能夠直接保存了 若是不挑剔 應該是可使用的了 可是發現一個問題 除了Android 10以上的 用 MediaStore 提示是廢棄的 矇蔽 爲何廢棄呢 咱們進入文檔看看url

inserting of images should be performed using {@link MediaColumns#IS_PENDING}, which offers richer control over lifecycle.
複製代碼

一看文檔一臉懵逼 沒看懂什麼 各類查閱資料 百度一堆都是廢棄的API。。。Android 這麼難嗎 保存個圖片 都沒有一個兼容的 完美的解決方案 或者 API 通過漫長的查詢 種算看到眉目了 改良後的代碼是這樣的spa

fun saveBitmap2Gallery2(context: Context, bitmap: Bitmap): Boolean {
 val name = System.currentTimeMillis().toString()  val photoPath = Environment.DIRECTORY_DCIM + "/Camera"  val contentValues = ContentValues().apply {  put(MediaStore.MediaColumns.DISPLAY_NAME, name)  put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  put(MediaStore.MediaColumns.RELATIVE_PATH, photoPath)//保存路徑  put(MediaStore.MediaColumns.IS_PENDING, true)  }  }  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  contentValues  ) ?: return false //爲空的話 直接失敗返回了  //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use {  it ?: return false  bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)  }  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  contentValues.put(MediaStore.MediaColumns.IS_PENDING, false)  }  return true  } 複製代碼

是簡單了好多 發現 在Android 10一下 只須要屏蔽 RELATIVE_PATHIS_PENDING 就能夠了code

可是發現一個問題 在Android 默認是保持在 /sdCard/Pictures/ 雖然說也存放圖片的地方 可是不是 /sdCard/DCIM/Camera小米 OV 等手機上 不能直接顯示在照片裏 而是在相冊 中的 Pictures裏 或者在所有照片也能夠查到 這個問題我仍是不知道怎麼解決 由於 在使用 ContentValues 時 10.0如下的系統是不能設置路徑的 那怎麼辦 可能仍是的用廢棄的 API 了 這裏方法提供了 2種 自行選擇最適合本身的 若是知道怎麼保存到DCIM 歡迎評論區解答下

最後須要注意的是 在Android 10 中 保存到相冊是不須要存儲權限的 在6 - 9的版本中 須要存儲權限

整合放出代碼

object PhotoUtils {
  fun saveBitmap2Gallery(context: Context, bitmap: Bitmap): Boolean {  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  //返回出一個URI  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  /*  這裏若是不寫的話 默認是保存在 /sdCard/DCIM/Pictures  */  ContentValues()//這裏能夠啥也不設置 保存圖片默認就行了  ) ?: return false //爲空的話 直接失敗返回了   //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use {  it ?: return false  bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)  }  return true  } else {  MediaStore.Images.Media.insertImage(context.contentResolver, bitmap, "title", "desc")  return true  }  }   fun saveFile2Gallery(context: Context, url: String): Boolean {  if (true) {  //返回出一個URI  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  /*  這裏能夠默認不寫 默認保存在  */  ContentValues()  ) ?: return false //爲空的話 直接失敗返回了  //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use { os ->  os ?: return false  var x = download(url, os)  return x  }  return false  } else {  val externalFilesDir =  context.getExternalFilesDir(Environment.DIRECTORY_DCIM) ?: return false  var name = "${System.currentTimeMillis()}.jpg"  val file = File(externalFilesDir, name)  //下載文件到應用目錄  download(url, file.outputStream())  MediaStore.Images.Media.insertImage(  context.contentResolver,  file.absolutePath,  name,  "desc"  )  //刷新相冊  context.sendBroadcast(  Intent(  Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,  Uri.fromFile(File(file.getPath()))  )  )  return true  }  }    fun saveFile2Gallery2(context: Context, url: String): Boolean {  val name = System.currentTimeMillis().toString()  val photoPath = Environment.DIRECTORY_DCIM + "/Camera"  val contentValues = ContentValues().apply {  put(MediaStore.MediaColumns.DISPLAY_NAME, name)  put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  put(MediaStore.MediaColumns.RELATIVE_PATH, photoPath)//保存路徑  put(MediaStore.MediaColumns.IS_PENDING, true)  }  }  //返回出一個URI  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  contentValues  ) ?: return false  //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use { os ->  os ?: return false  var x = download(url, os)  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  contentValues.put(MediaStore.MediaColumns.IS_PENDING, false)  }  return x  }  return false  }    fun saveBitmap2Gallery2(context: Context, bitmap: Bitmap): Boolean {  val name = System.currentTimeMillis().toString()  val photoPath = Environment.DIRECTORY_DCIM + "/Camera"  val contentValues = ContentValues().apply {  put(MediaStore.MediaColumns.DISPLAY_NAME, name)  put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  put(MediaStore.MediaColumns.RELATIVE_PATH, photoPath)//保存路徑  put(MediaStore.MediaColumns.IS_PENDING, true)  }  }  val insert = context.contentResolver.insert(  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  contentValues  ) ?: return false //爲空的話 直接失敗返回了  //這個打開了輸出流 直接保存圖片就行了  context.contentResolver.openOutputStream(insert).use {  it ?: return false  bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)  }  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  contentValues.put(MediaStore.MediaColumns.IS_PENDING, false)  }  return true  }    private fun download(url: String, os: OutputStream): Boolean {  val url = URL(url)  (url.openConnection() as HttpURLConnection).also { conn ->  conn.requestMethod = "GET"  conn.connectTimeout = 5 * 1000  if (conn.responseCode == 200) {  conn.inputStream.use { ins ->  val buf = ByteArray(2048)  var len: Int  while (ins.read(buf).also { len = it } != -1) {  os.write(buf, 0, len)  }  os.flush()  }  return true  } else {  return false  }  }  }  } 複製代碼
相關文章
相關標籤/搜索