You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android Wear异常等待循环问题求助

分析与解决你的智能手表整点震动线程随机卡住问题

Hey Luca, 这个随机卡住的问题我太熟悉了——大概率是Android的低功耗策略在搞鬼!咱们一步步拆解:

为什么会随机卡住?

  • 系统低功耗休眠(Doze模式/App Standby):当手表锁屏进入省电状态时,Android会限制后台线程的运行,甚至直接暂停非必要的后台任务。你的普通Thread没有持有唤醒锁,系统为了省电就会把它“冻住”,直到设备被唤醒(比如点亮屏幕)才会继续执行。这种情况只会在设备进入低功耗状态时触发,所以才会随机出现(比如电量充足时系统可能不严格限制,电量低时就会触发)。
  • 普通Thread的不可靠性:如果这段代码是在BroadcastReceiver里启动的线程,Receiver的生命周期极短,一旦Receiver执行完毕,系统会认为这个线程是“无主”的后台任务,更容易被优先级调度干掉。

具体解决方案

1. 给线程加上唤醒锁(最直接的修复)

在线程运行期间持有PARTIAL_WAKE_LOCK,确保CPU保持运行直到任务完成,避免被系统休眠。

第一步:添加权限

AndroidManifest.xml里声明唤醒锁权限:

<uses-permission android:name="android.permission.WAKE_LOCK" />

第二步:修改线程代码,加入唤醒锁逻辑

// 先声明一个唤醒锁变量(可以放在类里作为成员变量)
private PowerManager.WakeLock mHourlyChimeWakeLock;

// 你的线程逻辑修改如下:
final Thread t = new Thread() {
    @Override
    public void run() {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        // 获取唤醒锁,设置超时时间(比如10分钟,防止意外没释放导致耗电)
        mHourlyChimeWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HourlyChime:WakeLock");
        mHourlyChimeWakeLock.acquire(10 * 60 * 1000);

        Calendar cal = Calendar.getInstance();
        try {
            Log.d(PROGRAM, "Waiting until next full minute");
            cal.setTimeInMillis(System.currentTimeMillis());
            while(cal.get(Calendar.SECOND) >= (60 - (HOURLY_CHIME_CORRECTION / 1000))) {
                TimeUnit.SECONDS.sleep(1);
                cal.setTimeInMillis(System.currentTimeMillis());
            }
            hourlyChime();
        } catch(Exception e) {
            Log.i(PROGRAM, "Exception:\n" + e);
        } finally {
            // 无论是否异常,都要释放唤醒锁
            if (mHourlyChimeWakeLock != null && mHourlyChimeWakeLock.isHeld()) {
                mHourlyChimeWakeLock.release();
            }
        }

        alarmManager.cancel(hourlyChimePendingIntent);
        alarmManager.setExact(
            AlarmManager.RTC_WAKEUP, getNextHourMS(), hourlyChimePendingIntent);
    }
};
t.start();

2. 优化任务执行上下文(更稳定的长期方案)

如果你的闹钟是通过BroadcastReceiver触发的,Receiver的生命周期太短,不适合启动后台线程。推荐改用WorkManager来处理这个任务,它是Android官方推荐的后台任务管理工具,自动处理低功耗场景,还能保证任务被执行:

第一步:创建Worker类

public class HourlyChimeWorker extends Worker {
    private static final String TAG = "HourlyChimeWorker";
    private static final int HOURLY_CHIME_CORRECTION = 10000;

    public HourlyChimeWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + ":WakeLock");
        wakeLock.acquire(5 * 60 * 1000);

        try {
            Calendar cal = Calendar.getInstance();
            Log.d(TAG, "Waiting until next full minute");
            cal.setTimeInMillis(System.currentTimeMillis());
            while(cal.get(Calendar.SECOND) >= (60 - (HOURLY_CHIME_CORRECTION / 1000))) {
                TimeUnit.SECONDS.sleep(1);
                cal.setTimeInMillis(System.currentTimeMillis());
            }
            // 执行震动逻辑
            hourlyChime();

            // 重新设置下一次闹钟
            AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
            PendingIntent pendingIntent = // 这里获取你的hourlyChimePendingIntent
            alarmManager.cancel(pendingIntent);
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, getNextHourMS(), pendingIntent);

            return Result.success();
        } catch (Exception e) {
            Log.i(TAG, "Exception:\n" + e);
            return Result.retry();
        } finally {
            if (wakeLock != null && wakeLock.isHeld()) {
                wakeLock.release();
            }
        }
    }

    private void hourlyChime() {
        // 你的震动逻辑
    }

    private long getNextHourMS() {
        // 计算下一次整点前10秒的时间戳
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR_OF_DAY, 1);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, -10); // 整点前10秒
        return cal.getTimeInMillis();
    }
}

第二步:在闹钟触发时启动Worker

在你的BroadcastReceiver里:

WorkManager.getInstance(context).enqueue(
    new OneTimeWorkRequest.Builder(HourlyChimeWorker.class)
        .setConstraints(new Constraints.Builder()
            .setRequiresBatteryNotLow(true) // 可选,根据需求设置
            .build())
        .build()
);

3. 额外优化点

  • 对于Android 6.0+设备,建议使用AlarmManager.setExactAndAllowWhileIdle()替代setExact(),这样即使设备处于Doze模式,闹钟也能精确触发。
  • 检查getNextHourMS()的实现,确保计算的下一次闹钟时间是准确的整点前10秒,避免累积误差。

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

火山引擎 最新活动