首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 類比時鐘秒針卡頓、跳格問題深度排查指南

     編輯:程式設計綜合問答

現象描述

自訂 AnalogClock 繼承 View,使用 CountDownTimer 每秒觸發 invalidate() 重繪。實際運行時秒針間歇性停滯,隨後一次跳動多格(如跳 3 秒),體驗極差。

程式碼關鍵點回顧

  • MyCount extends CountDownTimer(10000, 1000):理論上每 1 秒 onTick 一次,10 秒後 onFinish 重啟。
  • onTick 內取得當前秒數,計算 mSecond = 6.0f * second,設旗標 mSeconds = true,呼叫 invalidate()。
  • onDraw 中根據 mSeconds 決定是否重新設定秒針邊界並繪製。

卡頓根因分析

  1. CountDownTimer 非精確定時:底層以 Handler.sendMessageDelayed 實現,受主線程訊息佇列擁塞影響,實際觸發間隔可能超過 1 秒。
  2. onFinish 重啟機制導致時間漂移:每 10 秒重建一次定時器,累積誤差越來越大。
  3. 主線程繪製負擔過重:onDraw 執行多次 canvas.rotate/save/restore、Drawable.setBounds/draw,若佈局複雜易掉幀。
  4. mSeconds 旗標邏輯缺陷:僅在 onTick 置 true,onDraw 首次繪製後即置 false,若同一秒內重繪多次(如視窗遮擋),秒針會閃爍或靜止。

建議優化方案

方案一:改用 Handler + Runnable 固定頻率驅動(推薦)

private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Runnable mTickRunnable = new Runnable() {
    @Override public void run() {
        mCalendar.setToNow();
        mSecond = 6.0f * mCalendar.second;
        mSeconds = true;
        invalidate();
        mHandler.postDelayed(this, 1000); // 固定 1 秒後再次執行
    }
};

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mHandler.post(mTickRunnable); // 啟動
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mHandler.removeCallbacks(mTickRunnable); // 停止
}

方案二:採用 Choreographer 同步 VSync 繪製(最流暢)

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override public void doFrame(long frameTimeNanos) {
        mCalendar.setToNow();
        mSecond = 6.0f * mCalendar.second + (mCalendar.get(Time.MILLISECOND) / 1000f) * 6.0f;
        invalidate();
        Choreographer.getInstance().postFrameCallback(this);
    }
};

通用優化建議

  • 將 Drawable 邊界計算移出 onDraw,僅在尺寸變更(onSizeChanged)時更新。
  • 啟用硬體加速:setLayerType(LAYER_TYPE_HARDWARE, null)(API 14+)。
  • 使用 Path 或 drawLine 直接繪製指針,替代 Drawable 降低開銷。
  • 在 onDraw 開頭加入 if (!mSeconds && !mChanged) return; 避免無效重繪。

調試技巧

在 onTick / doFrame 打印 Log.d("Clock", "tick: " + System.currentTimeMillis()),觀察實際觸發間隔;同時用 adb shell dumpsys gfxinfo <pkg> 檢查幀率分布。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved