目录

  • 问题背景
  • handler失效的原因
  • 排查了解到Doze机制
  • 锁屏后计时解决方案

问题背景

需要记录用户播放音频的时长,我在一个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() {            //每隔一秒会执行一次        }    };}

更多相关文章

  1. android开发实战之做手机号和邮编查询小程序
  2. 日本一恶意软件成功入侵270000部安卓手机
  3. Android手机终端长连接心跳检测自动化测试方案
  4. 为寻求新增长点 山寨之父MTK发力Android
  5. 联发科放弃千元Android市场?
  6. Android(安卓)怎样获得手机信息
  7. 【Shader】适合 Android(安卓)手机上 GrabPass 方法失效的热扭曲
  8. Android高效率编码-细节,控件,架包,功能,工具,开源汇总,你想要的这里
  9. Android(安卓)时间轴的实现

随机推荐

  1. [置顶] Android启动过程的上层实现
  2. Android原生模拟器运行ARM APP
  3. 6.Android EditText 技巧
  4. android arm64硬件实现加速crc32算法
  5. Android调用系统裁剪的实现方法
  6. Android HAL 开发 (4)
  7. Android Animation 高手必读 之一 Tweene
  8. Android force Locale
  9. Android(安卓)ApiDemos示例解析(85):Grap
  10. Android(安卓)图片切换,ImageSwitcher 学