动画基础:定时器分辨率解析
立即解锁
发布时间: 2025-08-18 00:50:47 订阅数: 4 

# 动画基础:定时器分辨率解析
## 1. 定时器选择与动画帧率
在动画开发中,选择合适的定时器对实现流畅动画至关重要。`javax.swing.Timer` 和 `java.util.Timer` 是常用的定时器。一般来说,对于 Swing 应用程序,`javax.swing.Timer` 是更好的选择,原因如下:
- 无需额外的 `TimerTask` 对象,简化了定时器编程。
- 能在事件调度线程(EDT)接收定时事件,简化了与 GUI 相关的操作。
### 1.1 分辨率与帧率的关系
定时器事件之间的时间间隔被称为分辨率,它决定了动画的帧率。例如,若动画的分辨率为 30 毫秒,那么动画的帧率约为 33 fps(1000 毫秒 / 30 毫秒 / 帧 = 33.3 fps)。
通常,要让用户感觉动画流畅,帧率应达到 20 至 30 fps 或更高。在这个速度或更高速度下,人眼会将动画视为连续的平滑运动。不过,不同类型的动画所需的帧率也有所不同:
- 电影通常以 24 fps 的帧率播放。
- 视频游戏的帧率通常与计算机显示器的刷新率一致,范围从 60 次/秒到 70、75、85 次/秒甚至更高。
- 一些淡入淡出动画在较低的帧率下也能呈现出平滑的效果。
### 1.2 影响帧率的因素
有两个重要因素可能会影响我们实现期望的帧率:
- **性能**:如果系统无法在每一帧完成所有动画任务,那么每帧的处理时间就会变长,导致帧率降低。
- **定时器分辨率**:在某些情况下,我们可能需要非常高的帧率,这就要求定时器具有非常低的分辨率。但不同的定时器可能无法满足这一要求。
### 1.3 性能问题的解决方案
针对性能问题,我们可以采取以下措施:
- **优化性能**:使用适当的技术来优化动画任务的性能。
- **减少每帧的工作量**:例如将非 GUI 处理任务分配到不同的线程中,或者降低某些操作的频率。
- **降低期望**:接受较低的帧率,只要动画看起来流畅即可。
## 2. 不同定时器的分辨率分析
### 2.1 `System.currentTimeMillis()` 和 `System.nanoTime()` 的分辨率
在 Windows 系统中,`System.currentTimeMillis()` 基于低分辨率的计时机制,返回的时间值仅精确到 16 毫秒的边界。例如,当测量一个实际耗时 5 毫秒的活动时,可能会得到如下结果:
| 实际经过时间 | `currentTimeMillis` 经过时间 |
| --- | --- |
| 0 | 0 |
| 5 | 0 |
| 10 | 0 |
| 15 | 0 |
| 20 | 16 |
| 25 | 16 |
| 30 | 16 |
| 35 | 32 |
而 `System.nanoTime()` 在同一系统上的精度约为 2 至 3 毫秒。
以下是一个测量 `System.currentTimeMillis()` 和 `System.nanoTime()` 时间的示例代码:
```java
void measureTimeFunctions(int increment, int max) {
long startTime = System.currentTimeMillis();
long startNanos = System.nanoTime();
long elapsedTimeActual = 0;
long elapsedTimeMeasured = 0;
long elapsedNanosMeasured = 0;
System.out.printf("sleep currentTimeMillis nanoTime\n");
while (elapsedTimeActual < max) {
try {
Thread.sleep(increment);
} catch (Exception e) {}
long currentTime = System.currentTimeMillis();
long currentNanos = System.nanoTime();
elapsedTimeActual += increment;
elapsedTimeMeasured = currentTime - startTime;
elapsedNanosMeasured =
(currentNanos - startNanos) / 1000000;
System.out.printf(" %3d %4d %4d\n",
elapsedTimeActual, elapsedTimeMeasured,
elapsedNanosMeasured);
}
}
```
当 `increment` 设置为 5,`max` 设置为 50 时,运行结果如下:
| sleep | currentTimeMillis | nanoTime |
| --- | --- | --- |
| 5 | 16 | 8 |
| 10 | 16 | 15 |
| 15 | 31 | 21 |
| 20 | 31 | 27 |
| 25 | 31 | 33 |
| 30 | 47 | 38 |
| 35 | 47 | 44 |
| 40 | 63 | 50 |
| 45 | 63 | 56 |
| 50 | 63 | 62 |
从结果可以看出,`currentTimeMillis()` 的值以 15 至 16 毫秒的增量递增,而 `nanoTime()` 更接近理论上的 `sleep()` 时间,但随着总睡眠时间的增加,与 `sleep()` 时间的差距也会逐渐增大。
### 2.2 `Thread.sleep()` 的分辨率
为了准确测量 `Thread.sleep()` 的分辨率,我们可以多次短时间睡眠,然后将理论睡眠时间(每次睡眠时间乘以睡眠次数)与实际测量的睡眠时间进行比较。
以下是测量 `Thread.sleep()` 分辨率的代码:
```java
private void measureSleep() {
System.out.printf(" " +
"measured\n");
System.out.printf("sleep time iterations total time" +
" per-sleep\n");
for (int sleepTime = 0; sleepTime <= 20; ++sleepTime) {
int iterations = (sleepTime == 0) ? 10000 :
(1000 / sleepTime);
long startTime = System.nanoTime();
for (int i = 0; i < iterations; ++i) {
try {
Thread.sleep(sleepTime);
} catch (Exception e) {
}
}
long endTime = System.nanoTime();
long totalTime = (endTime - startTime) / 1000000;
float calculatedSleepTime = totalTime / (float)iterations;
System.out.printf(" %2d %5d %4d" +
" %5.2f\n", sleepTime, iterations,
totalT
```
0
0
复制全文
相关推荐










