返回顶部
首页 > 资讯 > 移动开发 >利用Android设计一个倒计时组件
  • 737
分享到

利用Android设计一个倒计时组件

2024-04-02 19:04:59 737人浏览 安东尼
摘要

目录1 背景2 对比分析2.1 是否是倒计时2.2 支持多任务2.3 支持时间校准2.4 支持同帧刷新2.5 支持延迟执行2.6 支持CPU休眠3 需求目标4 设计类结构5 具体实现

1 背景

我们在项目中经常有倒计时的场景,比如活动倒计时、抢红包倒计时等等。通常情况下,我们实现倒计时的方案有Android中的CountDownTimerJava中自带的TimerScheduleExcutorServiceRxJava中的interval操作符。 在实际项目中存在2个典型的问题,一是倒计时的实现形式不统一,不统一的原因分为认知不一致、每种倒计时方案各有优势;二是存在大量倒计时同时执行。

2 对比分析

关于几种方案的用法不是本文要讨论的重点,在此我们通过表格的方式列出来各自的特性,表格底部的CountDownTimerManager就是本文要为大家介绍的新鲜出炉的中心化倒计时组件。

2.1 是否是倒计时

Rx中的interval操作符是每隔一段时间会发送一个事件,可以说是一个计数器,而不是倒计时,在实际项目中会发现很多同学都把它当做倒计时在使用。下图是RxJava官方对interval的图解:

interval.png *The Interval operator returns an Observable that emits an infinite sequence of ascending integers, with a constant interval of time of your choosing between emissions.(简单理解就是固定间隔时间进行回调)

通过源码,我们也可以看出在ObservableInterval中实际也是进行了周期性调度。


public final class ObservableInterval extends Observable<Long> {

    @Override
    public void subscribeActual(Observer<? super Long> observer) {
        IntervalObserver is = new IntervalObserver(observer);
        observer.onSubscribe(is);

        Scheduler sch = scheduler;

        if (sch instanceof TrampolineScheduler) {
            Worker worker = sch.createWorker();
            is.setResource(worker);
            // 以给定的初始时间延迟、周期时间进行周期性执行
            worker.schedulePeriodically(is, initialDelay, period, unit);
        } else {
            // 以给定的初始时间延迟、周期时间进行周期性执行
            Disposable d = sch.schedulePeriodicallyDirect(is, initialDelay, period, unit);
            is.setResource(d);
        }
    }

那么作为倒计时使用会有什么问题呢?

问题一是回调可能不准确,假设倒计时9.5秒,每1秒刷新一次view,该怎么设置回调间隔时间呢?

问题二是在手机长时间息屏后,某些厂商会将CPU休眠,RxJavainterval操作符此时将被按下暂停键,当APP再次回到前台,interval会继续执行,假设暂停时倒计时剩余100秒,回到前台后实际只有10秒了,但是interval还是从100继续执行。

2.2 支持多任务

Timer是单线程串行执行多任务,假设taskA设定1秒后执行,taskB设定2秒后执行,实际上taskB是在taskA执行结束后才执行taskB,所以taskB的执行时间是在第3秒,所以Timer只算是伪支持多任务。ScheduledExecutorService是利用线程池支持了多任务调度的。

2.3 支持时间校准

CountDownTimer中每次onTick()方法回调,都会重新计算下一次onTick的时间。其中主要优化有2点,一是减去onTick执行耗时;二是针对特殊情况(如1.2.1中提到的手机息屏后CPU休眠场景),对比delay是否小于0,如果小于0则需要累加mCountdownInterval。


    long lastTickStart = SystemClock.elapsedRealtime();
    onTick(millisLeft);
    long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
    long delay;
    if (millisLeft < mCountdownInterval) {
        // 减去上面onTick方法执行耗时
        delay = millisLeft - lastTickDuration;
        if (delay < 0) {
            delay = 0;
        } else {
            delay = mCountdownInterval - lastTickDuration;
            // 针对特殊情况(如1.2.1中提到的手机息屏后CPU休眠场景)
            // 对比delay是否小于0,如果小于0则需要累加mCountdownInterval
            while (delay < 0) {
               delay += mCountdownInterval;
            }
        }
        sendMessageDelayed(obtainMessage(MSG), delay);
     }

2.4 支持同帧刷新

我们项目中有很多场景是这样的:

倒计时A先执行,倒计时B后执行,A和B的倒计时结束时间是一致的,那么我们假设倒计时时间为10秒,每1秒刷新一次,A在剩余10秒时执行,B在剩余9.5秒执行,当二者在同一页面显示时,就会刷新不一致,这个问题在我们新的倒计时组件中将得到解决,文章后面将会详细说明。

2.5 支持延迟执行

延迟1分钟再执行10秒的倒计时?Android中提供的CountDownTimer是做不到的,只能额外写一个1分钟的定时器,到时间后再启动倒计时。

2.6 支持CPU休眠

我们这里提到的支持CPU休眠,并不是指CPU休眠期间倒计时仍能得到执行,而是在CPU休眠后能够恢复正常执行。和1.2.3中提到的时间校准类似,解决了时间校准的问题也就支持了CPU休眠的特性。

3 需求目标

  • 设计一个中心化的倒计时组件,同时支持上述提到的一系列特性。
  •  接口易于调用,使用者只需关注计时回调的逻辑。

4 设计类结构

CountDownTimer采用静态内部类形式实现单例,暴露countdown() timer()方法供业务方ClientA/ClientB/ClientC等调用,Task是抽象任务,每次调用countdown() timer()后都生成一个task,交给优先级队列管理,内部通过handler不断从队列中取task执行。

5 具体实现

5.1 收口

收口可以理解为进行统一管理,这里我们通过一个优先级队列管理所有倒计时、定时器,优先级队列可以直接采用Java中已有的数据结构PriorityQueue,设置队列大小默认为5,根据task中的mExecuteTimeInNext进行正序排序。这里有一个特别需要注意的点,PriorityQueue需要传入实现Comparator接口的对象,在实现Comparator时,因为mExecuteTimeInNext的数据类型是long类型,而compare()方法返回的是int类型,如果直接使用二者相减再强制转换为int,会有溢出的风险,所以可以使用Long.compare()来实现大小比较。


  
  private final Queue<Task> mTaskQueue = new PriorityQueue<>(DEFAULT_INITIAL_CAPACITY,
      new Comparator<Task>() {
        @Override
        public int compare(Task task1, Task task2) {
          // return (int) (task1.mExecuteTimeInNext - task2.mExecuteTimeInNext); 错误示范
          return Long.compare(task1.mExecuteTimeInNext, task2.mExecuteTimeInNext);
        }
      });

5.2 支持与RxJava协同

提供倒计时countdown、定时器timer操作符,直接返回Observable,方便与RxJava框架协同。


  
  public synchronized Observable<Long> countdown(long millisInFuture, long countDownInterval, long delayMillis) {
    AtomicReference<Task> taskAtomicReference = new AtomicReference<>();
    return Observable.create((ObservableOnSubscribe<Long>) emitter -> {
      Task newTask = new Task(millisInFuture, countDownInterval, delayMillis, emitter);
      taskAtomicReference.set(newTask);
      synchronized (CountDownTimerManager.this) {
        Task topTask = mTaskQueue.peek();
        if (topTask == null || newTask.mExecuteTimeInNext < topTask.mExecuteTimeInNext) {
          cancel();
        }
        mTaskQueue.offer(newTask);
        if (mCancelled) {
          start();
        }
      }
    }).doOnDispose(() -> {
      if (taskAtomicReference.get() != null) {
        taskAtomicReference.get().dispose();
      }
    });
  }


  
  public synchronized Observable<Long> timer(long millisInFuture) {
    return countdown(0, 0, millisInFuture);
  }

  private synchronized void remove(Task task) {
    mTaskQueue.remove(task);
    if (mTaskQueue.size() == 0) {
      cancel();
    }
  }

5.3 支持时间校准

不推荐使用RxJava中的interval,因为RxJava中的实现无法保障倒计时的准确执行,如在手机CPU进入休眠之后再恢复到前台。那么如何实现呢?这里借鉴了AndroidCountDownTimer的设计思路,在每次onTick后重新计算了下一次onTick的时间,比如前文提到的“CPU进入休眠”的情况,我们通过一个while循环,计算出下一次onTick的时间(其条件是大于当前时间)。


          mTaskQueue.poll();
          if (!task.isDisposed()) {
            if (stopMillisLeft <= 0 || task.mCountdownInterval == 0) {
              task.mDisposed = true;
              task.mEmitter.onNext(0L);
              task.mEmitter.onComplete();
            } else {
              task.mEmitter.onNext(stopMillisLeft % task.mCountdownInterval == 0 ? stopMillisLeft
                  : (stopMillisLeft / task.mCountdownInterval + 1) * task.mCountdownInterval);
              // 时间校准 
              // special case:
              // user's onTick took more than interval to complete
              // cpu slept
              do {
                task.mExecuteTimeInNext += task.mCountdownInterval;
              } while (task.mExecuteTimeInNext < SystemClock.elapsedRealtime());
              mTaskQueue.offer(task);
            }
          }

5.4 支持同步刷新

针对多个倒计时在同一时刻结束的情况,优化了刷新不同步的问题。 mExecuteTimeInNext是下一次任务执行时间,假设倒计时剩余时间为9.5秒,每1秒刷新,那么下一次的执行时间则是在0.5秒之后。


    private Task(long millisInFuture, long countDownInterval, long delayMillis,
        @NonNull ObservableEmitter<Long> emitter) {
      mCountdownInterval = countDownInterval;
      // 计算出下次执行的时间
      mExecuteTimeInNext = SystemClock.elapsedRealtime() + (mCountdownInterval == 0 ? 0
          : millisInFuture % mCountdownInterval) + delayMillis;
      mStopTimeInFuture = SystemClock.elapsedRealtime() + millisInFuture + delayMillis;
      mEmitter = emitter;
    }

5.5 支持延迟执行

在计算下次执行的时间时,加上了delayMillis,这样就支持了延迟执行。


    private Task(long millisInFuture, long countDownInterval, long delayMillis,
        @NonNull ObservableEmitter<Long> emitter) {
      mCountdownInterval = countDownInterval;
      // 计算出下次执行的时间
      mExecuteTimeInNext = SystemClock.elapsedRealtime() + (mCountdownInterval == 0 ? 0
          : millisInFuture % mCountdownInterval) + delayMillis;
      mStopTimeInFuture = SystemClock.elapsedRealtime() + millisInFuture + delayMillis;
      mEmitter = emitter;
    }

到此这篇关于利用Android设计一个倒计时组件的文章就介绍到这了,更多相关利用Android设计倒计时组件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 利用Android设计一个倒计时组件

本文链接: https://lsjlt.com/news/136672.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 利用Android设计一个倒计时组件
    目录1 背景2 对比分析2.1 是否是倒计时2.2 支持多任务2.3 支持时间校准2.4 支持同帧刷新2.5 支持延迟执行2.6 支持CPU休眠3 需求目标4 设计类结构5 具体实现...
    99+
    2024-04-02
  • 如何利用momentJs做一个倒计时组件
    今天就跟大家聊聊有关如何利用momentJs做一个倒计时组件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。vue和moment做的一个倒计时展示样式:<template>...
    99+
    2023-06-22
  • 利用Android实现一个倒计时功能
    这篇文章给大家介绍利用Android实现一个倒计时功能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。先上效果图activity_main.xml<&#63;xml version="1.0&qu...
    99+
    2023-05-31
    android roi
  • vue如何设计一个倒计时秒杀的组件
    这篇文章将为大家详细讲解有关vue如何设计一个倒计时秒杀的组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。简介:倒计时秒杀组件在电商网站中层出不穷  不过思路...
    99+
    2024-04-02
  • 利用momentJs做一个倒计时组件(实例代码)
    今天抽空给大家介绍下vue和moment做的一个倒计时,具体内容如下所示: 展示样式: <template> <div class="table-ri...
    99+
    2024-04-02
  • React怎么实现一个倒计时hook组件
    这篇“React怎么实现一个倒计时hook组件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React怎么实现一个倒计时ho...
    99+
    2023-07-05
  • Android实现一个倒计时自定义控件
    目录(一)前言(二)效果展示(三)实现思路(三)代码地址总结(一)前言 Android 其实提供了一个倒计时控件叫做CountDownTimer,这个倒计时控件用起来也很简单,但是要...
    99+
    2024-04-02
  • 怎么在Android中利用CountDownTimer实现一个倒计时功能
    这篇文章主要介绍了怎么在Android中利用CountDownTimer实现一个倒计时功能,此处通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考价值,需要的朋友可以参考下:Android是什么Android是一种基于Lin...
    99+
    2023-05-30
    android countdowntimer
  • 利用java怎么实现一个倒计时功能
    这期内容当中小编将会给大家带来有关利用java怎么实现一个倒计时功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。代码如下:package timer;import java.util.Calendar;...
    99+
    2023-05-31
    java ava
  • 利用Java怎么编写一个倒计时功能
    利用Java怎么编写一个倒计时功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1.简易方式实现 import java.util.*; import java...
    99+
    2023-05-31
    java ava
  • Android利用Chronometer实现倒计时功能
    项目需要实现一个计时的功能,利用Chronometer虽然可以很方便的实现计时功能,但需要的却是一个倒计时控件。 百度了一下方法不少,倒计时的却没有,于是用Chronomete...
    99+
    2022-06-06
    倒计时 Android
  • 怎么在Android中利用控件实现一个验证码倒计时功能
    这期内容当中小编将会给大家带来有关怎么在Android中利用控件实现一个验证码倒计时功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。使用方式<com.landptf.view.CountDownM...
    99+
    2023-05-31
    android roi
  • React实现一个倒计时hook组件实战示例
    目录前言思路实现总结前言 本篇文章主要实现一个无样式的倒计时 hook 组件,通常不同地方的倒计时样式都不同,但倒计时的逻辑基本是都是一样的,因此可以抽离成一个工具方法或者一个 ...
    99+
    2023-02-23
    React倒计时hook组件 React hook
  • 怎么在Android应用中利用View实现一个倒计时功能
    这篇文章将为大家详细讲解有关怎么在Android应用中利用View实现一个倒计时功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Android 自定义View实现倒计时需求:具体方法如下:...
    99+
    2023-05-31
    android roi view
  • Android如何实现一个倒计时自定义控件
    这篇“Android如何实现一个倒计时自定义控件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android如何实现一个倒计...
    99+
    2023-06-29
  • android倒计时控件示例
    本文为大家分享了android倒计时控件,供大家参考,具体代码如下 package com.ly.sxh.view; import android.content.Cont...
    99+
    2022-06-06
    示例 Android
  • 用python写一个简单的倒计时软件
    模块:time import time count = 0 a = int(input('time:')) while (count < a): count_now = a - count print(cou...
    99+
    2023-01-31
    倒计时 简单 软件
  • angular倒计时组件如何使用
    这篇文章主要介绍“angular倒计时组件如何使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“angular倒计时组件如何使用”文章能帮助大家解决问题。组件cou...
    99+
    2024-04-02
  • Android中怎么实现一个倒计时效果
    今天就跟大家聊聊有关Android中怎么实现一个倒计时效果,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。需求:a.在后台添加时,如果是今日直播,则需要添加开始时间(精确到秒);b.离...
    99+
    2023-05-30
    android
  • Android实现一个完美的倒计时功能
    目录一. 已有倒计时方案存在的问题 1. CountDownTimer 2. Handler 3. Timer 二. 自己封装倒计时 总结一. 已有倒计时方案存在的问题 在开发倒计...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作