Android Wear OS 2.0应用振动偶尔失效问题排查求助
Hey there! Let's dig into this tricky vibration issue you're facing with your first Android Wear app—resource constraints and lifecycle quirks on Wear OS can throw some unexpected curveballs, so let's break down what's likely going wrong and how to fix it.
What's Probably Causing the One-Time Vibration Glitch
Looking at your code, a few key patterns stand out as potential culprits:
Multiple AlarmActivity Instances Clashing
When yourSmsReceiverdetects a new message whileAlarmActivityis already active, it launches a new instance of the activity (thanks to Android's defaultstandardlaunch mode). This triggers the old instance to enteronStop()/onPause()(settingactivetofalse), while the new instance starts fresh. The system might interrupt the existing vibration during this switch, or overlapping vibrator calls could cause unexpected behavior that stops the vibration prematurely.Unreliable Static
activeFlag
The staticactiveboolean is shared across allAlarmActivityinstances. When an old instance is stopped, it setsactivetofalse—even if a new instance is already running. This can break yourSmsReceiverlogic and lead to missed vibration restarts.WakeLock Leaks
You only release the wake lock when the stop button is clicked. IfAlarmActivitygets destroyed unexpectedly (e.g., due to low memory on Wear OS), the wake lock stays held, straining system resources and potentially causing the vibrator service to misbehave.
Fixes to Try (Start with the First One!)
Let's tackle these issues step by step:
1. Use singleTop Launch Mode to Avoid Multiple Instances
First, update your AndroidManifest.xml to set AlarmActivity to singleTop mode—this ensures only one instance runs at the top of the stack, and new launch requests trigger onNewIntent() instead of creating a duplicate:
<activity android:name=".AlarmActivity" android:launchMode="singleTop"> </activity>
Then add onNewIntent() to AlarmActivity to reactivate vibration if it's interrupted, plus clean up resource lifecycle:
private Vibrator vibrator; private PowerManager.WakeLock wakeLock; private boolean isVibrating = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_alarm); // Initialize vibrator and wake lock once vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); wakeLock = powerManager.newWakeLock( PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyWakelockTag" ); wakeLock.acquire(); // Start vibration on first launch startVibration(); // Stop button logic (update to track vibration state) Button stopButton = findViewById(R.id.stop_button); stopButton.setOnClickListener(v -> { if (isVibrating) { vibrator.cancel(); isVibrating = false; } if (wakeLock.isHeld()) { wakeLock.release(); } new SendEmail().execute(); startActivity(new Intent(AlarmActivity.this, MainActivity.class)); }); setAmbientEnabled(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // Reactivate vibration if it stopped unexpectedly startVibration(); // Refresh wake lock to keep screen on if (!wakeLock.isHeld()) { wakeLock.acquire(); } } private void startVibration() { if (!isVibrating && vibrator.hasVibrator()) { long[] mVibratePattern = new long[]{0, 400, 800, 600, 800, 800, 800, 1000}; int[] mAmplitudes = new int[]{0, 255, 0, 255, 0, 255, 0, 255}; vibrator.vibrate(VibrationEffect.createWaveform(mVibratePattern, mAmplitudes, 0)); isVibrating = true; } } // Clean up resources when the activity is destroyed @Override protected void onDestroy() { super.onDestroy(); if (wakeLock.isHeld()) { wakeLock.release(); } if (isVibrating) { vibrator.cancel(); } }
2. Replace the Static active Flag with a Foreground Check
Instead of relying on a static boolean, use ActivityManager to check if AlarmActivity is actually in the foreground in your SmsReceiver:
private boolean isAlarmActivityActive(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1); if (!tasks.isEmpty()) { ComponentName topActivity = tasks.get(0).topActivity; return topActivity.getClassName().equals("hu.asd.watchtest.AlarmActivity"); } return false; }
Then update your receiver's else-if branch:
else if (isAlarmActivityActive(context)) { Intent intent1 = new Intent(); intent1.setClassName("hu.asd.watchtest", "hu.asd.watchtest.AlarmActivity"); intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); context.startActivity(intent1); }
3. Add Lifecycle Hooks for Vibration State
Update your AlarmActivity lifecycle methods to track vibration state properly:
@Override protected void onPause() { super.onPause(); // Pause vibration when the activity is hidden (prevents background vibration) if (isVibrating) { vibrator.cancel(); isVibrating = false; } } @Override protected void onResume() { super.onResume(); // Resume vibration when the activity comes back to foreground startVibration(); }
Why These Fixes Should Work
By using singleTop, you eliminate duplicate activity instances that were causing conflicting vibrator calls. The isVibrating flag ensures you don't start overlapping vibrations, and proper wake lock cleanup prevents resource leaks that could destabilize the system on Wear OS.
Give these changes a shot—they should resolve the random one-time vibration stops you're seeing!
内容的提问来源于stack exchange,提问作者adamb

