Android 锁屏后handler计时失效

问题背景

需要记录用户播放音频的时长,我在一个service中开启handler,每隔一秒用户正在播放,将播放时长++;正常播放都没问题,即使锁屏大部分手机记录都准确,但在华为手机9.0系统中发现锁屏记录时间不准确,接下来就有了一大堆的排查。

handler失效的原因

采用每次都将系统时间和变量进行写入到手机内存中,通过查看时间和变量看问题出在哪里:记录的结果如下,数据正常就是每隔一秒变量正好加1
在这里插入图片描述
在记录不准确的手机中获取的数据如下图:变量是每次都加1,但是时间确实有时候出现跳动,两组数据之前有时候相差好几秒,问题就出在这里了,所以导致记录不准确。
在这里插入图片描述
根据排查发现handler本来设置的是每隔一秒执行一次,但有时会出现好几秒后才执行一次,在看到handler源码时发现它是通过SystemClock.uptimeMillis()+delayMillis从消息队列获取消息的,
delayMillis:就是间隔时间 如1秒
SystemClock.uptimeMillis():表示从开机到现在的毫秒数(手机睡眠的时间不包括在内)
如果手机短暂休眠,导致SystemClock.uptimeMillis()值没变,那么uptimeMillis值没变可能导致handler定时失效。
在这里插入图片描述
在这里插入图片描述
下面是往手机文件写内容的代码,不覆盖内容,续写内容。

public void saveStudyToFile(String string) {
        String filePath = LOCAL_PATH + "clockFile";
        File file = new File(filePath);
        try {
            // 首先判断文件夹是否存在
            if (!file.exists()) {
                if (!file.mkdirs()) {   // 文件夹不存在则创建文件
                    Toast.makeText(MyApplication.getInstance(), "文件夹创建失败", Toast.LENGTH_SHORT).show();
                }
            } else {
                File fileWrite = new File(filePath + File.separator + "studyLog.txt");
                // 实例化对象:文件输出流=====要想覆盖之前的内容,去掉ture即可
                FileOutputStream fileOutputStream = new FileOutputStream(fileWrite, true);
                // 写入文件
                fileOutputStream.write(string.getBytes());
                // 清空输出流缓存
                fileOutputStream.flush();
                // 关闭输出流
                fileOutputStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

排查了解到Doze机制

在排查是了解到Android 6.0系统引入Doze机制保护电池,延长电池寿命。
Doze模式可以简单理解为低能耗状态,一些无关运行能停止都停止了。

Doze机制特征是:
1、在息屏30分钟内、手机没有移动并且没有正在充电状态,会进入Doze模式;
2、进入Doze模式后手机会停止网络请求操作,WakeLocks会被忽略失效,AlarmManager会被推迟,系统不再进行WiFi扫描等等;
3、进入Doze模式后,手机会每隔一段时间进入30s的活动区,这段时间手机检测是否有需要处理的操作;
4、进入Doze模式后,Doze模式没有被唤醒,它会逐渐进入深度深眠;

这里借用网上的一张图更好的理解Doze模式:
在这里插入图片描述

锁屏后计时解决方案

虽然了解了那么多,但是发现并没解决锁屏后计时不准的问题,那看看我的解决方法吧。

1、使用Timer替换handler(Timer在应用存活期有效,休眠时无法唤醒)
2、设置手机锁屏开屏监听,采用 System.currentTimeMillis()获取锁屏的时间。

下面是监听锁屏的服务代码,至于注册和开启服务自己完善就没问题了。

/**
 * 监听锁屏的服务
 */
public class LockService extends Service {
    private BroadcastReceiver receiver;
    private long screenOffTime;
    private static float screenOffStudyTime;


    @Override
    public void onCreate() {
        super.onCreate();
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction() == Intent.ACTION_SCREEN_OFF) {
                    LogUtil.e("收到锁屏广播");
                    screenOffTime = System.currentTimeMillis();
                    screenOffStudyTime = AlbumClockDBUtils.durationNum;
                } else if (intent.getAction() == Intent.ACTION_SCREEN_ON) {
                    LogUtil.e("屏幕亮起广播");
                    //锁屏这段时间时长 秒
                    long soffTime = (System.currentTimeMillis() - screenOffTime) / 1000;
                    //最新学习时长
                    int screenOffTime = (int) (soffTime + screenOffStudyTime);

                }
            }
        };
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        registerReceiver(receiver, filter);
        timer = new Timer(true);
        timer.schedule(timerTask, 0, 1000); //延时1000ms后执行,立即执行
    }

    private Timer timer;
    TimerTask timerTask = new TimerTask() {
        public void run() {
            //每隔一秒会执行一次
        }
    };
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值