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
相关产品推荐
相关产品推荐

