Android 11 踩雷之 App无法唤起相机

本文介绍了在Android 11上遇到的App无法唤起相机的问题及其解决方案。由于Android 11对存储权限进行了更改,直接创建文件会导致权限不足。解决方法包括在Manifest中声明相机权限,并使用Android 11推荐的方式来保存文件。此外,还提供了一个Kotlin的简洁权限申请工具类,用于方便地申请相机、读写等权限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android 11 踩雷之 App无法唤起相机

因为本人申请了MIUI12的开发版,所以体验了Android11,讲真的体验感极差,基本上App很少有适配android11 的 举个例子大多数游戏的分享会报错,部分App保存图片时会造成闪退等。

好了,言归正传,我们先来看问题
在这里插入图片描述
这是在吊起相机时出现的问题,问题代码是
在这里插入图片描述

  // 在sd卡中创建一保存图片(原图和缩略图共用的)文件夹
    private fun createFileIfNeed(fileName: String): File {
        val fileA = this.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath + "/test"
        val fileJA = File(fileA)
        if (!fileJA.exists()) {
            fileJA.mkdirs()
        }
        val file = File(fileA, fileName)
        if (!file.exists()) {
            file.createNewFile()
        }
        return file
    }

我们先来看看为什么会出现这个问题
在这里插入图片描述
android11 将存储空间分为了2部分,所以直接调用就会导致,权限不足而导致崩溃。

解决方法:
首先权限申请方面:在Android11之前,在文件里只需要相册相册的权限,在manifest里声明相机的权限,然后用相机的调用码,就可以打开相机,进行拍照裁剪等操作。

但是在android11中这是不允许的,需要在manifest文件中声明,而且需要申请相机权限。

然后就是代码中彻底解决:
在这里插入图片描述
将这一行替换掉
替换成
在这里插入图片描述

因为Android11 更新后,不存在公共分区,所以无法在公共分区创建文件。

 // 在sd卡中创建一保存图片(原图和缩略图共用的)文件夹
    private fun createFileIfNeed(fileName: String): File {
        val fileA = this.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath + "/test"
        val fileJA = File(fileA)
        if (!fileJA.exists()) {
            fileJA.mkdirs()
        }
        val file = File(fileA, fileName)
        if (!file.exists()) {
            file.createNewFile()
        }
        return file
    }

到现在问题解决。

最后给大家分享一下,kotlin的简便权限申请,简单又好用

object PermissionUtils {
    private val RESULT_CODE_TAKE_CAMERA = 7461    //拍照
    private val RESULT_CODE_OPEN_ALBUM = 7462     //读写
    private val RESULT_CODE_SOUND_RECORD = 7463   //录音
    private val RESULT_CODE_CALL_PHONE = 10111   //拨号
    lateinit var context: Context
    private var cameraCallback: (() -> Unit)? = null        //相机回调
    private var readAndWriteCallback: (() -> Unit)? = null  //读写回调
    private var audioCallback: (() -> Unit)? = null         //录音回调
    private var callPhone: (() -> Unit)? = null         //录音回调

    /**
     * 电话权限申请
     */
    fun CallPhone(context: Context, callPhone: () -> Unit) {
        this.context = context
        this.callPhone = callPhone
        Log.e("测试电话权限申请", "ok")
        permission(context, Manifest.permission.CALL_PHONE, RESULT_CODE_CALL_PHONE, callPhone)
    }

    /**
     * 相机权限申请
     */
    fun camera(context: Context, cameraCallback: () -> Unit) {
        this.context = context
        this.cameraCallback = cameraCallback
        permission(context, Manifest.permission.CAMERA, RESULT_CODE_TAKE_CAMERA, cameraCallback)
    }


    /**
     * 读写权限申请
     */
    fun readAndWrite(context: Context, readAndWriteCallback: () -> Unit) {
        this.context = context
        this.readAndWriteCallback = readAndWriteCallback
        permissions(
            context,
            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA),
            RESULT_CODE_OPEN_ALBUM,
            readAndWriteCallback
        )
    }

    /**
     * 录音权限申请
     */
    fun audio(context: Context, audioCallback: () -> Unit) {
        this.context = context
        this.audioCallback = audioCallback
        permission(
            context,
            Manifest.permission.RECORD_AUDIO,
            RESULT_CODE_SOUND_RECORD,
            audioCallback
        )
    }

    /**
     * 权限申请结果
     */
    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        val cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED
        Log.e("测试电话权限申请3", "ok")
        when (requestCode) {

            RESULT_CODE_TAKE_CAMERA -> {    //拍照
                if (cameraAccepted) {
                    cameraCallback?.let { it() }
                } else {
                    //用户拒绝
                    Toast.makeText(context, "请开始拍照权限", Toast.LENGTH_SHORT).show()
                }
            }
            RESULT_CODE_OPEN_ALBUM -> { //读写
                if (cameraAccepted) {
                    readAndWriteCallback?.let { it() }
                } else {
                    Toast.makeText(context, "请开启应用读取权限", Toast.LENGTH_SHORT).show()
                }
            }
            RESULT_CODE_SOUND_RECORD -> { //录音
                if (cameraAccepted) {
                    audioCallback?.let { it() }
                } else {
                    Toast.makeText(context, "请开启应用录音权限", Toast.LENGTH_SHORT).show()
                }
            }
            RESULT_CODE_CALL_PHONE -> { //拨号
                if (cameraAccepted) {
                    callPhone?.let {
                        Log.e("已申请电话权限", "ok")
                        it()
                    }
                } else {
                    Log.e("未申请电话权限", "ok")
                    Toast.makeText(context, "请开启应用电话权限", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    //权限申请
    private fun permission(
        context: Context,
        systemCode: String,
        resultCode: Int,
        callback: () -> Unit
    ) {
        //判断是否有权限
        if (ContextCompat.checkSelfPermission(
                context,
                systemCode
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            Log.e("已申请权限", "ok")
            callback()
        } else {
            //申请权限
            Log.e("无权限", "ok")
            ActivityCompat.requestPermissions(context as Activity, arrayOf(systemCode), resultCode)
        }
    }  //权限申请

    private fun permissions(
        context: Context,
        systemCode: Array<String>,
        resultCode: Int,
        callback: () -> Unit
    ) {
        //判断是否有权限
        if (ContextCompat.checkSelfPermission(
                context,
                systemCode[0]
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            Log.e("已申请权限", "ok")
            callback()
        } else {
            //申请权限
            Log.e("无权限", "ok")
            ActivityCompat.requestPermissions(context as Activity, systemCode, resultCode)
        }
    }
}

调用的时候也很简单

PermissionUtils.readAndWrite(this, {

        })

到此,本文结束,谢谢大家耐心的阅读。

在UniApp中,直接自定义相机的取景框并不是标准的API提供功能。因为UniApp本质上是运行在Webview中,大部分复杂的交互和界面都是由原生应用接管的,包括相机的控制。不过,你可以在Android或iOS平台上使用原生组件来实现这样的需求,然后通过H5+ Native的方式在UniApp中集成。 对于Android,你可以使用CameraX API 来创建自定义的相机界面,但这需要对Android开发有深入了解,并且不是单纯的JavaScript能完成的,需要在原生端编写Java或Kotlin代码。 对于iOS,则可以借助AVFoundation框架,结合Swift或Objective-C来修改UIImagePickerController,但同样需要在原生环境中做相应的修改。 举个简化的例子(在iOS中Objective-C): ```swift import UIKit import AVFoundation class CustomCameraViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { var captureSession: AVCaptureSession? override func viewDidLoad() { super.viewDidLoad() // 创建一个自定义的 AVCaptureVideoPreviewLayer 并添加到视图上 let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!) view.layer.addSublayer(previewLayer) // 更改预览层的frame和border width previewLayer.frame = view.bounds previewLayer.borderWidth = 4.0 previewLayer.borderColor = UIColor.black.cgColor } func presentCamera() { let imagePicker = UIImagePickerController() imagePicker.delegate = self imagePicker.sourceType = .camera imagePicker.cameraCaptureMode = .photo present(imagePicker, animated: true, completion: nil) } // ... 其他UIImagePickerControllerDelegate和UINavigationControllerDelegate方法 } ``` 这只是一个基本的例子,实际操作会涉及更多复杂的配置和错误处理。而且,这仅限于在iOS中,Android的部分代码将完全不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风伴佳人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值