android10分区存储照片选择,拍照处理

前言

在前段时间,google正式发布了android10, Scoped storage(分区存储) 这个功能在android9上就跃跃欲试, 在android10上呼之欲出,某些app甚至在上架之后还会出现targetAPI降低的神奇操作,本文不针对于分区存储介绍etc ,不提及其它解决方案, 仅仅说明笔者在开发过程中遇到的关于图片处理问题以及解决方法, 先说下当前如何解决问题.

兼容模式

如果targetAPI == 29
常规的访问存储目录,或者做相关操作都会出现权限拒绝的异常

android:requestLegacyExternalStorage ="true"

在manifest文件application标签下加入这句话则会回到传统存储模式

如果targetAPI == 28

android:requestLegacyExternalStorage ="false"

在manifest文件application标签下加入此项配置为false , 则会强制开启分区存储, android10上需要适配的工作在android9上也需要

如果targetAPI > 29
配置会失效,google在说明此项配置只针对于临时问题

选取照片适配

先上代码 (笔者一直用kt , java同学应该也不难看懂,有疑问可以留言),这是常规的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为相机输出路径,后面会提及, ?.let句式为内联函数非空判断
            REQUEST_CODE_OPEN_PHOTO_ALBUM -> if (resultCode == RESULT_OK && data != null) {
                val resolver = applicationContext.contentResolver //在一般情况此处都用contentResolver来直接query文件的absPath然后进行上传操作
                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来获取文件流 -> 转化字节码上传 ,retrofit2也正好支持, 挺整好,旧版本也行

将resolver.openInputStream(it).use{}替换成如下这一段代码会更加简洁

val resolver = applicationContext.contentResolver
        resolver.openFileDescriptor(uri, "r")?.let { pfd ->//获取ParcelFileDescriptor一样可以获取字节码
            mPresenter.uploadFile(FileUtil.getScaledBitmapBytes(pfd))
        }

附上FileUtil片段,缩放图片&转换为字节码

    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) }
    }

拍照处理

先上代码,看调起相机片段

    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的方式 ,各位自行选取

上班摸鱼所写.时间紧迫没来得及整理文稿,都是现学现卖,请键下留情,不懂的可以留言,谢看
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容