You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android本地音频波形可视化用Visualizer组件崩溃求助

安卓Visualizer初始化错误码-3崩溃问题排查与解决

问题描述

尝试使用android.media.audiofx.Visualizer实现本地音频波形可视化时,程序持续崩溃,核心报错信息:

E/visualizers-JNI: Visualizer initCheck failed -3
E/Visualizer-JAVA: Error code -3 when initializing Visualizer.
Caused by: java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -3

已尝试操作:

  • 添加RECORD_AUDIO和MODIFY_AUDIO_SETTINGS权限
  • 重启设备
  • 将API级别升级至29+
  • 使用MediaPlayer.OnPreparedListener延迟初始化Visualizer

核心实现代码如下:

MainActivity核心代码

class MainActivity : AppCompatActivity() {
    fun onPlayClick(view: View) {
        val song = "/path/that/definitelyexists.mp3"
        playMusic(song)
    }

    fun playMusic(audioPath: String){
        val mediaPlayerInstance = MediaPlayer()
        mediaPlayerInstance.setAudioAttributes(
            AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build()
        )
        mediaPlayerInstance.setDataSource(audioPath)
        
        mediaPlayerInstance.setOnPreparedListener(object: MediaPlayer.OnPreparedListener {
            override fun onPrepared(mp: MediaPlayer?) {
                mp ?: return
                val visualiz = findViewById<Visualizer>(R.id.musicitviz)
                visualiz.setAudioSessionId(mp.audioSessionId)
                mp.start()
            }
        })
        mediaPlayerInstance.prepare()
    }
}

自定义Visualizer View代码

class Visualizer(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    private val paint = Paint()
    private var visualizer: android.media.audiofx.Visualizer? = null
    private var waveformByteArray: ByteArray? = null

    init {
        paint.color = Color.rgb(244, 40, 30)
    }

    override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        waveformByteArray?.let {
            if(it.isEmpty()) {
                Log.d("Visualizer", "waveformbytearray is empty.")
                return
            } else {
                Log.d("Visualizer", "waveformbytearray is not empty.")
            }
        }
    }

    fun setAudioSessionId(audioSessionId: Int) {
        visualizer?.release()
        val newVisualizer = android.media.audiofx.Visualizer(audioSessionId)
        newVisualizer.enabled = true
        newVisualizer.captureSize = android.media.audiofx.Visualizer.getCaptureSizeRange()[1]
        newVisualizer.setDataCaptureListener(object: android.media.audiofx.Visualizer.OnDataCaptureListener {
            override fun onWaveFormDataCapture(
                visualizer: android.media.audiofx.Visualizer?,
                waveform: ByteArray?,
                samplingRate: Int
            ) {
                waveformByteArray = waveform
                Log.d("Visualizer", "Updating waveform")
                invalidate()
            }

            override fun onFftDataCapture(
                visualizer: android.media.audiofx.Visualizer?,
                fft: ByteArray?,
                samplingRate: Int
            ) {}
        }, android.media.audiofx.Visualizer.getMaxCaptureRate() / 2, true, false)
        newVisualizer.enabled = true
        visualizer = newVisualizer
    }
}

布局代码

<com.musicit.music.Visualizer
    android:id="@+id/musicitviz"
    android:layout_width="267dp"
    android:layout_height="258dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

构建配置

android {
    compileSdk 33

    defaultConfig {
        applicationId "com.musicit.music"
        minSdk 29
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    namespace 'com.musicit.music'
}

解决方案

错误码-3对应Visualizer.ERROR_INIT_FAILED,通常由权限缺失、音频会话无效或系统限制导致,按以下步骤修复:

  • 动态申请RECORD_AUDIO权限
    Android 6.0+(API 23)开始,RECORD_AUDIO属于危险权限,仅在Manifest声明无效,需运行时申请:

    private val RECORD_AUDIO_REQUEST_CODE = 1001
    
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)
          if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
              != PackageManager.PERMISSION_GRANTED) {
              ActivityCompat.requestPermissions(
                  this,
                  arrayOf(Manifest.permission.RECORD_AUDIO),
                  RECORD_AUDIO_REQUEST_CODE
              )
          }
      }
    
      override fun onRequestPermissionsResult(
          requestCode: Int,
          permissions: Array<out String>,
          grantResults: IntArray
      ) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults)
          if (requestCode == RECORD_AUDIO_REQUEST_CODE) {
              if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                  // 权限已授予,可正常使用Visualizer
              } else {
                  Toast.makeText(this, "需要录音权限以显示音频波形", Toast.LENGTH_SHORT).show()
              }
          }
      }
    
  • 匹配Visualizer与MediaPlayer生命周期
    确保MediaPlayer完全就绪后初始化Visualizer,且在页面销毁时释放资源:
    在自定义Visualizer View中添加释放方法:

    fun release() {
          visualizer?.let {
              it.enabled = false
              it.release()
              visualizer = null
          }
      }
    

    在MainActivity的onDestroy中调用:

    private var mediaPlayerInstance: MediaPlayer? = null
    
      override fun onDestroy() {
          super.onDestroy()
          findViewById<Visualizer>(R.id.musicitviz).release()
          mediaPlayerInstance?.release()
      }
    

    注意:将MediaPlayer改为Activity成员变量,避免局部变量被提前回收。

  • 验证音频会话ID有效性
    在onPrepared中打印音频会话ID,确认不为0:

    Log.d("AudioSession", "ID: ${mp.audioSessionId}")
    

    若ID为0,检查音频文件路径是否正确、文件是否具备读取权限。

  • 处理系统限制
    部分设备可能限制第三方应用使用Visualizer,可尝试:

    • 确保设备扬声器未静音,Visualizer需音频输出激活
    • 切换音频输出设备(如从耳机切换到扬声器)后重试
    • 检查系统隐私设置是否阻止应用访问音频数据

内容的提问来源于stack exchange,提问作者haxwagores

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:09:58