大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
实现原理
拿CountDownTimer的源代码看一下,并不复杂,基本上是对Handler的封装,使用send/post delay。这套机制仍然首先于Handler的原理,所以在精度上也不能够保证很精确,只能保证不会早于预期执行。详见我另外一篇介绍Handler send/post delay的文章: Handler sendMessageDelayed()/postDelayed()机制详解。
源代码:
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Schedule a countdown until a time in the future, with
* regular notifications on intervals along the way.
*
* Example of showing a 30 second countdown in a text field:
*
* <pre class="prettyprint">
* new CountDownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
* }
*
* public void onFinish() {
* mTextField.setText("done!");
* }
* }.start();
* </pre>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {@link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
public abstract class CountDownTimer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
* @param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
看其中两个关键点:
(1)Handler的创建
// handles counting down
private Handler mHandler = new Handler() {...};
作为一个私有的非静态成员变量,这段代码执行在构造方法中,也就是说,Handler是在调用构造方法(new)的线程中被创建,并且是通过不带参数的构造方法Handler()。从Handler的源代码可以看到,这种情况会调用到:
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
通过Looper.myLooper()得到Looper对象并且校验非空,空则抛出RE。Looper.myLooper():
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
由此可见,普通工作线程因为没有准备好Looper,所以显然是不能直接创建CountDownTimer对象的。
(2)Handler的消息处理/分发
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
handleMessage()方法:
首先做剩余时间计算:
final long millisLeft = mStopTimeInFuture – SystemClock.elapsedRealtime();
mStopTimeInFuture是在倒计时开始方法start()中计算得到:
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mMillisInFuture是CountDownTimer构造方法中传入的参数,即倒计时的时长。
所以millisLeft计算出的是当前handleMessage()被执行时候,距离倒计时结束的剩余时间。
接下来的逻辑:
(1)如果发现剩余时间<=0,意味着倒计时已经结束,直接调用onFinish()。这里注意到,可能是会超过最后时限(即<0的情况)才执行onFinish()。原因也可参见我另外一篇 Handler sendMessageDelayed()/postDelayed()机制详解。
(2)否则,如果剩余时间不足一次onTick间隔,直接以剩余时间sendMessageDelayed()。
(3)否则(这条逻辑分支是最关键的逻辑,实现onTick的循环调用):
a)首先记下一个当前的时间戳,即本次执行onTick开始的时间;
b)执行onTick(),注意,这里是同步执行;
c)执行完onTick()以后,再看一下当前的时间情况
// take into account user’s onTick taking time to execute
long delay = lastTickStart + mCountdownInterval – SystemClock.elapsedRealtime();
因为不能够确认同步执行的onTick()会耗时多少,所以对当前时间需要校验:
// special case: user’s onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
以onTick间隔作为时间单元向下跳,直到delay为非负值,也就是说还没有流失掉的onTick执行时间距离,并以这个值sendMessageDelayed()下一条Msg。
总结一下,可以看出:
(1)每次handleMessage的时候,实际上是以当前时间值为基础,计算一下剩余的时间并决定接下来的处理逻辑,这样做的目的显然是在现行Android Handler机制里面,无法保证以绝对准时的delay时间处理Msg。
(2)对于同步onTick的处理很严谨,考虑到了onTick可能的耗时操作。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/194105.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...