MediaRecorder Android 11 启动失败 -1004

MediaRecorder Android 11 start failed -1004

在 Android 11 我的 MediaRecorder 初始化失败。我怀疑问题与 scopedstorage 有关,但我一直无法找出原因。我正在使用 MediaRecorder 从麦克风录制音频。我从音频中提取振幅,所以我无意保留文件,所以路径是/dev/null

 var mRecorder: MediaRecorder? = null


 if (mRecorder == null) {
        mRecorder = MediaRecorder()
        mRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
        mRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
        mRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
        mRecorder!!.setOutputFile("/dev/null")
        try {
            mRecorder!!.prepare()
        } catch (ioe: IOException) {
            Log.e("[Monkey]", "IOException: " + Log.getStackTraceString(ioe))
        } catch (e: SecurityException) {
            Log.e("[Monkey]", "SecurityException: " + Log.getStackTraceString(e))
        }
        try {
            mRecorder!!.start()
        } catch (e: SecurityException) {
            Log.e("[Monkey]", "SecurityException: " + Log.getStackTraceString(e))
        }

崩溃发生在 MediaRecorded.start()。 /dev/null 不是 Android 11 上的有效路径吗?

Logcat:

start failed: -1004
2020-11-15 10:51:41.827 11836-11836/= E/AndroidRuntime: FATAL EXCEPTION: main
    Process: c=, PID: 11836
    java.lang.RuntimeException: start failed.
        at android.media.MediaRecorder.start(Native Method)
 

将“/dev/null”替换为正确的文件路径“${externalCacheDir.absolutePath}/test.3gp”,它应该可以工作。

出于某种原因,设置 "/dev/null" 路径以防止 MediaRecorder 存储导致 Android 11 版本崩溃,截至目前,设置它得到固定的实际路径就像另一个答案中提到的那样,但在我的情况下,我在我的应用程序中需要的只是注册来自麦克风的幅度水平,所以为了防止不确定地存储在输出文件中我刷新了每分钟 MediaRecorder 对象。

这是一个完整但简化的示例:

class MainActivity : AppCompatActivity() {

    private lateinit var mediaRecorder: MediaRecorder
    private var handler: Handler = Handler()

    private var fakeOutput = ""
    private var needsFlush = false
    private var flushCounter = 300

    private val runnable = object : Runnable {
        override fun run() {
            Log.d("AudioAmplitude: ", mediaRecorder.maxAmplitude.toString())
            if (needsFlush) {
                if (flushCounter == 0) {
                    mediaRecorder.stop()
                    mediaRecorder.release()
                    initMediaRecorder()
                    flushCounter = 300
                }
                flushCounter--
            }
            handler.postDelayed(this, 200)
        }
    }

    private val requestedPermissions = arrayOf(
        Manifest.permission.RECORD_AUDIO,
        Manifest.permission.CAMERA
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestedPermissions.forEach {
            ContextCompat.checkSelfPermission(
                this,
                it
            )
        }

        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.RECORD_AUDIO
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    (this as Activity?)!!,
                    Manifest.permission.RECORD_AUDIO
                )
            ) {
            } else {
                ActivityCompat.requestPermissions(
                    (this as Activity?)!!, arrayOf(Manifest.permission.RECORD_AUDIO),
                    0
                )
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            fakeOutput = "${externalCacheDir?.absolutePath}/temp.3gp"
            needsFlush = true
        } else {
            fakeOutput = "/dev/null"
        }

        initMediaRecorder()
    }

    private fun initMediaRecorder() {
        mediaRecorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setOutputFile(fakeOutput)
            prepare()
            start()
        }
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onResume() {
        super.onResume()
        mediaRecorder.resume()
        handler.post(runnable)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onPause() {
        super.onPause()
        mediaRecorder.pause()
        handler.removeCallbacks(runnable)
    }
}

请注意,我使用处理程序(可能不是最佳做法)打印出 maxAmplitude,这是我从事此工作的主要原因,我正在使用它来每 60 秒刷新一次(处理程序每​​ 200 毫秒调用一次计数器,计数器为 300 减一)。