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

Android振动器多次调用vibrate后停止振动的问题排查与解决

解决Android振动持续中断的问题

看起来你的核心问题是多次触发振动时,系统或自身代码导致振动随机停止,而且尝试先cancel()再启动的话会导致第二条短信来就停振。我来帮你分析问题并给出优化方案:

问题根源分析

  1. 静态变量的状态冲突:active和isVibrating是静态变量,当多次收到短信启动AlarmActivity时,多个Activity实例会共享这些静态变量,导致状态混乱(比如一个实例把isVibrating设为false,另一个实例误以为需要重新启动振动)。
  2. 重复调用振动导致中断:每次收到短信都会调用vibrate(),而Android的Vibrator服务同一时间只能处理一个振动任务,新的vibrate()调用会直接终止之前的振动,这就是你加cancel()后第二条短信来就停振的原因。
  3. 生命周期方法的错误调用:onPause()里调用vibrate()是不合理的,当Activity失去焦点时重新启动振动,会和当前正在运行的振动产生冲突。
  4. 系统短信通知的振动抢占:默认的短信通知会触发系统振动,抢占Vibrator资源,导致你的应用振动被中断。

代码优化方案

1. 修正状态管理,避免多实例冲突

把静态变量改为实例变量,同时确保AlarmActivity每次启动时复用已有实例,而不是创建新的:

  • 在启动AlarmActivity的Intent中添加标记,确保单实例:
// 短信接收器中启动AlarmActivity的代码
Intent alarmIntent = new Intent(context, AlarmActivity.class);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(alarmIntent);
  • 修改AlarmActivity中的变量:
// 移除static修饰符
private boolean active = false;
private boolean isVibrating = false;

2. 控制振动启动逻辑,避免重复触发

修改vibrate()方法,只有当前未在振动时才启动新的振动:

public void vibrate() {
    if (isVibrating) {
        return; // 已经在振动,直接返回,避免重复启动
    }
    if (vibrator == null) {
        Log.d("AlarmActivity", "初始化振动器");
        vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
    }
    // 检查振动权限(Android 6.0+需要动态申请,不过振动权限是正常权限,一般默认授予)
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            vibrator.vibrate(VibrationEffect.createWaveform(mVibratePattern, mAmplitudes, 0));
        } else {
            // 兼容低版本,不支持振幅设置
            vibrator.vibrate(mVibratePattern, 0);
        }
        isVibrating = true;
    }
}

3. 修正生命周期方法的错误调用

移除onPause()中调用vibrate()的代码,确保只有在Activity启动时才触发振动:

@Override
public void onPause() {
    super.onPause();
    active = false;
    // 移除这行:if (isVibrating) { vibrate(); }
}

4. 拦截系统短信通知,避免振动抢占

如果你的应用已经在处理指定号码的短信,可以拦截系统的默认短信通知,防止系统触发振动:

  • 在短信广播接收器中设置较高优先级,并调用abortBroadcast()(仅适用于Android 7.0以下的有序广播):
// 旧版本有序广播的处理(Android < 7.0)
@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            Object[] pdus = (Object[]) bundle.get("pdus");
            if (pdus != null) {
                for (Object pdu : pdus) {
                    SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
                    String sender = smsMessage.getDisplayOriginatingAddress();
                    if ("你的指定号码".equals(sender)) {
                        // 处理你的逻辑,启动AlarmActivity
                        // 拦截系统广播,阻止默认通知
                        abortBroadcast();
                    }
                }
            }
        }
    }
}
  • Android 7.0及以上:android.provider.Telephony.SMS_RECEIVED是无序广播,无法通过abortBroadcast()拦截,此时可以考虑使用SMS Retriever API,或者引导用户将你的应用设为默认短信应用,这样系统就不会触发默认的短信通知。

5. 优化WakeLock管理

确保WakeLock在振动期间一直持有,直到用户点击停止按钮:

  • 在onStart()中获取WakeLock时,增加超时时间(可选,防止意外无法释放):
wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyWakelockTag");
// 设置超时时间,比如10分钟,避免一直持有
wakeLock.acquire(600000);
  • 在停止按钮的点击事件中,确保释放WakeLock并更新状态:
stopButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        if (vibrator != null) {
            vibrator.cancel();
        }
        isVibrating = false;
        if (wakeLock != null && wakeLock.isHeld()) {
            wakeLock.release();
        }
        // 后续逻辑...
    }
});

额外测试建议

  • 测试时关闭其他可能触发振动的应用(如短信、微信等),排除外部干扰。
  • 查看Logcat中Vibrator相关的日志,看是否有系统级的振动中断提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:25:48