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

Android VibrationManager cancel方法无法停止振动问题排查与修复

问题原因分析
  1. API 31+ 振动取消逻辑不匹配:Android 12(API 31)及以上版本中,调用VibratorManager.vibrate(CombinedVibration, VibrationAttributes)会返回一个VibrationToken,作为本次振动的唯一标识。当前代码使用无参cancel()尝试取消所有振动,但在无限循环振动场景下,系统无法精准关联到目标振动实例,导致取消失败。
  2. 无限循环振动的系统处理特性:你使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:07:34