android10分區(qū)存儲照片選擇,拍照處理

前言

在前段時(shí)間,google正式發(fā)布了android10, Scoped storage(分區(qū)存儲) 這個(gè)功能在android9上就躍躍欲試, 在android10上呼之欲出,某些app甚至在上架之后還會出現(xiàn)targetAPI降低的神奇操作,本文不針對于分區(qū)存儲介紹etc ,不提及其它解決方案, 僅僅說明筆者在開發(fā)過程中遇到的關(guān)于圖片處理問題以及解決方法, 先說下當(dāng)前如何解決問題.

兼容模式

如果targetAPI == 29
常規(guī)的訪問存儲目錄,或者做相關(guān)操作都會出現(xiàn)權(quán)限拒絕的異常

android:requestLegacyExternalStorage ="true"

在manifest文件application標(biāo)簽下加入這句話則會回到傳統(tǒng)存儲模式

如果targetAPI == 28

android:requestLegacyExternalStorage ="false"

在manifest文件application標(biāo)簽下加入此項(xiàng)配置為false , 則會強(qiáng)制開啟分區(qū)存儲, android10上需要適配的工作在android9上也需要

如果targetAPI > 29
配置會失效,google在說明此項(xiàng)配置只針對于臨時(shí)問題

選取照片適配

先上代碼 (筆者一直用kt , java同學(xué)應(yīng)該也不難看懂,有疑問可以留言),這是常規(guī)的pick操作, 沒什么好說的

private fun photoFromGallery() {
        try {
            val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Intent(Intent.ACTION_OPEN_DOCUMENT)
            } else {
                Intent(Intent.ACTION_PICK)
            }
            intent.type = "image/*"
            startActivityForResult(intent, REQUEST_CODE_OPEN_PHOTO_ALBUM)
        } catch (e: ActivityNotFoundException) {
            e.printStackTrace()
            showToast(R.string.open_photo_album_error)
        }

    }

然后來看看activityResult怎么處理,慣例先上代碼

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            REQUEST_CODE_TAKE_PHOTOS -> if (resultCode == RESULT_OK)
                mOutPutUri?.let { mPresenter.uploadFile(it) } //mOutPutUri為相機(jī)輸出路徑,后面會提及, ?.let句式為內(nèi)聯(lián)函數(shù)非空判斷
            REQUEST_CODE_OPEN_PHOTO_ALBUM -> if (resultCode == RESULT_OK && data != null) {
                val resolver = applicationContext.contentResolver //在一般情況此處都用contentResolver來直接query文件的absPath然后進(jìn)行上傳操作
                data.data?.let {
                    resolver.openInputStream(it).use { stream ->
                        stream?.readBytes()?.let { bytes -> mPresenter.uploadFile(bytes, System.currentTimeMillis().toString() + ".png") }
                    }
                }
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }

注意看這一段

resolver.openInputStream(it).use { stream ->//此處的stream為自動回收流,不可引用
                        stream?.readBytes()?.let { bytes -> mPresenter.uploadFile(bytes, System.currentTimeMillis().toString() + ".png") }
                    }

resolver.openInputStream(it).use來獲取文件流 -> 轉(zhuǎn)化字節(jié)碼上傳 ,retrofit2也正好支持, 挺整好,舊版本也行

將resolver.openInputStream(it).use{}替換成如下這一段代碼會更加簡潔

val resolver = applicationContext.contentResolver
        resolver.openFileDescriptor(uri, "r")?.let { pfd ->//獲取ParcelFileDescriptor一樣可以獲取字節(jié)碼
            mPresenter.uploadFile(FileUtil.getScaledBitmapBytes(pfd))
        }

附上FileUtil片段,縮放圖片&轉(zhuǎn)換為字節(jié)碼

    fun getScaledBitmapBytes(pfd: ParcelFileDescriptor, needRecycle: Boolean = true): ByteArray? {
        val opt = BitmapFactory.Options()
        opt.inJustDecodeBounds = true
        BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor, null, opt)
        var inSampleSize = 1
        val height = opt.outHeight
        val width = opt.outWidth
        if (width > 720 || height > 720) {//長或者寬>720則等比縮放
            inSampleSize = if (width >= height) {
                height / 720
            } else {
                width / 720
            }
        }
        opt.inJustDecodeBounds = false
        opt.inSampleSize = inSampleSize
        val bitmap = BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor, null, opt)
        if (needRecycle)
            pfd.close()
        return bitmap?.let { bmpToByteArray(it, needRecycle) }
    }

拍照處理

先上代碼,看調(diào)起相機(jī)片段

    private fun photoFromCamera() {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)//通過MediaStore訪問
        intent.resolveActivity(packageManager)?.let {
            mOutPutUri = FileUtil.insertExternalStorageImage(this)
            mOutPutUri?.also {
                intent.putExtra(MediaStore.EXTRA_OUTPUT, it)
                startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTOS)
            } ?: showToast("路徑生成失敗")
        } ?: showToast("沒有可用的拍照程序")
    }

附上FileUtil片段 , MediaStore插入圖片操作

    fun insertExternalStorageImage(context: Context): Uri? {
        return context.applicationContext.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, ContentValues().apply {
            val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(Date())
            put(MediaStore.Images.Media.DISPLAY_NAME, "${JPEG_FILE_PREFIX}${timeStamp}${JPEG_FILE_SUFFIX}")
            put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        })
    }

通過上述片段可獲取content Uri媒體路徑, 然后可以照葫蘆畫瓢, 一樣可以用流的方式或者ParcelFileDescriptor的方式 ,各位自行選取

上班摸魚所寫.時(shí)間緊迫沒來得及整理文稿,都是現(xiàn)學(xué)現(xiàn)賣,請鍵下留情,不懂的可以留言,謝看
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容