Android VibrationManager cancel方法无法停止振动问题排查与修复
问题原因分析
- API 31+ 振动取消逻辑不匹配:Android 12(API 31)及以上版本中,调用
VibratorManager.vibrate(CombinedVibration, VibrationAttributes)会返回一个VibrationToken,作为本次振动的唯一标识。当前代码使用无参cancel()尝试取消所有振动,但在无限循环振动场景下,系统无法精准关联到目标振动实例,导致取消失败。 - 无限循环振动的系统处理特性:你使用
VibrationEffect.createWaveform(timeInterval, 1)创建了无限循环的振动(repeatIndex=1表示从索引1开始重复波形),这类持续振动需要精准匹配对应的标识才能被终止,无参cancel()的兼容性不足。
修复方案
核心思路是保存每次振动的唯一标识,取消时精准终止目标振动:
1. 修改振动方法,返回振动标识
更新vibrate方法,返回对应版本的振动标识(API 31+返回VibrationToken,低版本返回Vibrator实例):
fun Context.vibrate(wave: Boolean): Any? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { (getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).run { vibrate( CombinedVibration.createParallel(makeEffect(wave)), VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build() ) } } else { @Suppress("DEPRECATION") (getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrate(makeEffect(wave)) } else { @Suppress("DEPRECATION") vibrate(if (wave) 1000 else 500) } } } } @RequiresApi(Build.VERSION_CODES.O) private fun makeEffect(wave: Boolean): VibrationEffect { return if (wave) { VibrationEffect.createWaveform(timeInterval, 1) } else { VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE) } } private val timeInterval = longArrayOf(60, 120, 180, 240, 420, 480)
2. 修改停止方法,精准取消振动
更新stopVibration方法,传入保存的振动标识,针对不同版本执行精准取消:
fun Context.stopVibration(vibrationToken: Any?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { (getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).apply { // 优先使用token精准取消,兜底取消所有应用振动 (vibrationToken as? VibrationToken)?.let { cancel(it) } ?: cancel() } } else { @Suppress("DEPRECATION") if (vibrationToken is Vibrator) { if (vibrationToken.hasVibrator()) vibrationToken.cancel() } else { // 兜底逻辑:取消当前应用所有振动 (getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).apply { if (hasVibrator()) cancel() } } } }
3. 业务层调用示例
在启动振动时保存标识,停止时传入该标识:
// 启动振动并保存标识 val vibrationToken = context.vibrate(wave = true) // 需要停止振动时调用 context.stopVibration(vibrationToken)
内容的提问来源于stack exchange,提问作者Moshe Katz
相关产品推荐
相关产品推荐

