Jank cho ứng dụng di động — nó là gì, nguyên nhân và giải pháp

Tác giả: IT Sectr Đã đăng: 2026-04-01 Thời gian đọc: 10 phút

Jank là thuật ngữ chỉ hiện tượng giật lag rõ rệt hoặc “nói lắp” trong hoạt ảnh giao diện do bỏ qua các khung hình riêng lẻ. Trong ứng dụng di động, Jank xảy ra khi thời gian kết xuất khung hình vượt quá ngân sách được phân bổ theo tốc độ làm mới màn hình. Theo Android Developers, 2025, Jank là nguyên nhân chính gây ra cảm giác chủ quan về “sự chậm chạp” — ứng dụng có thể hoàn hảo về chức năng, nhưng người dùng cảm thấy nó chậm do FPS không ổn định.

Những điểm chính

  • Jank — khung hình bị bỏ qua, xuất hiện dưới dạng giật lag hoạt ảnh.
  • Nguyên nhân chính là vượt quá ngân sách thời gian cho mỗi khung hình (16,6 ms cho 60 FPS).
  • Jank phát sinh do Layout nặng, GC kéo dài, chặn luồng chính hoặc overdraw.
  • Để chẩn đoán, sử dụng FrameTimeline (Android) và Instruments (iOS).
  • Loại bỏ Jank giúp tăng NPS và tỷ lệ giữ chân người dùng lên 15–25%.

Jank là gì

Jank là thuật ngữ trong đồ họa máy tính chỉ một khiếm khuyết thị giác khi hoạt ảnh chuyển động giật cục thay vì trượt mượt mà. Trong phát triển di động, Jank được đo bằng số khung hình bị bỏ qua (skipped frames) trên một đơn vị thời gian. Nếu hệ thống không kịp chuẩn bị khung hình trước thời điểm VSync, màn hình sẽ lặp lại khung hình trước đó — xảy ra hiện tượng tạm dừng 16,6 ms ở 60 Hz. Một khung hình bị bỏ qua có thể không được chú ý, nhưng một chuỗi 3–5 khung hình bị bỏ qua liên tiếp tạo ra cảm giác “giật lag” kéo dài 50–80 ms mà người dùng nhận thấy rõ ràng.

Jank đặc biệt quan trọng đối với các hoạt ảnh cần chạy với tốc độ không đổi: cuộn feed, hoạt ảnh mở menu, hiệu ứng parallax, chuyển tiếp giữa các màn hình. Theo nghiên cứu UX của Google (2024), ứng dụng có tỷ lệ Jank trên 3% số phiên cuộn nhận được nhiều hơn 22% đánh giá một sao so với ứng dụng có tỷ lệ dưới 0,5%. Công cụ Android Vitals tự động theo dõi Jank và phân loại theo mức độ nghiêm trọng: trung bình, nặng và nguy kịch.

Nguyên nhân chính của Jank

Nguyên nhân của Jank được chia thành nhiều loại. Đầu tiên là Layout Jank: gây ra bởi các lệnh gọi requestLayout() thường xuyên do thay đổi kích thước View, hoạt ảnh LayoutTransition hoặc tải nội dung động. Mỗi lệnh gọi requestLayout kích hoạt Measure + Layout cho toàn bộ cây con View, có thể mất 5–30 ms. Thứ hai là Draw Jank: liên quan đến overdraw và sử dụng drawable nặng. Thứ ba là Thread Jank: chặn luồng chính do các thao tác đồng bộ — tải tệp, thao tác cơ sở dữ liệu trên luồng chính, giải mã Bitmap.

Loại thứ tư là GC Jank: thu gom rác (Garbage Collection) trong ART/Dalvik hoặc Swift ARC. Khi nhiều đối tượng tích tụ trong heap, GC bắt đầu tạm dừng Stop-The-World kéo dài 5–15 ms. Trên Android, các lần tạm dừng GC thường xảy ra nhất khi cấp phát thường xuyên trong vòng lặp: tạo đối tượng trong onDraw(), cấp phát trong bộ điều hợp, biểu thức lambda không sử dụng. Thứ năm là IPC Jank: giao tiếp liên tiến trình (ContentProvider, Binder) trên luồng chính. Thứ sáu là Rendering Jank: kết xuất GPU chậm do shader không tối ưu hoặc kết cấu có kích thước lớn.

Loại JankNguyên nhânThời lượng điển hìnhCông cụ phát hiện
LayoutrequestLayout, relayout5–30 msPerfetto, Systrace
DrawOverdraw, drawable nặng3–20 msGPU Profiling
ThreadChặn luồng chính10–200 msAndroid Studio Profiler
GCGarbage Collection5–15 msMemory Profiler
RenderingTải GPU10–50 msGPU Tracer, Xcode GPU

Chẩn đoán Jank trong Android

Trong Android, chẩn đoán Jank bắt đầu với dấu vết hệ thống Perfetto. Perfetto ghi lại hoạt động của tất cả các luồng, CPU, GPU và bộ lập lịch. Chỉ báo rõ ràng của Jank là các dòng Choreographer.doFrame và Choreographer.doCallbacks: nếu khoảng cách giữa hai lệnh gọi doFrame liên tiếp vượt quá 16,6 ms, một khung hình đã bị bỏ qua. Perfetto hiển thị nguyên nhân chính xác — lệnh gọi hệ thống, khóa hoặc GC nào gây ra độ trễ. Trong Android Studio Profiler, chức năng tương tự có sẵn qua CPU Profiler.

Để phát hiện Jank tự động trong môi trường sản xuất, FrameMetricsAggregator được sử dụng — một API thu thập thống kê cho mỗi khung hình và tổng hợp theo phiên. Trong Android 12+, PerformanceHintManager đã xuất hiện — một API để gợi ý cho hệ thống về tốc độ khung hình mục tiêu. Nếu ứng dụng chỉ ra rằng nó đang chạy trong kịch bản 120 FPS, hệ thống có thể tăng tần số CPU/GPU để ngăn chặn Jank. Để ghi nhật ký đơn giản tất cả các khung hình bị bỏ qua, chỉ cần đăng ký Choreographer.FrameCallback.

Ghi nhật ký Jank qua Choreographer

Mã Kotlin đăng ký Choreographer.FrameCallback và ghi nhật ký từng khung hình bị bỏ qua với thời lượng trễ. Callback được gọi tại mỗi VSync.

kotlin
class JankDetector {

    private val frameBudget = 16_666_666L
    private var previousFrameTime = 0L

    private val callback =
        Choreographer.FrameCallback { currentTime ->
            if (previousFrameTime != 0L) {
                val frameDuration =
                    currentTime - previousFrameTime
                val skippedFrames =
                    (frameDuration / frameBudget) - 1
                if (skippedFrames > 0) {
                    Log.w("Jank",
                        "Skipped $skippedFrames frames")
                }
            }
            previousFrameTime = currentTime
            Choreographer.getInstance()
                .postFrameCallback(this)
        }

    fun start() {
        Choreographer.getInstance()
            .postFrameCallback(callback)
    }
}

Chẩn đoán Jank trong iOS

Trong iOS, chẩn đoán Jank được thực hiện qua Instruments với mẫu Core Animation. Instruments hiển thị FPS theo thời gian thực, số lần kết xuất ngoài màn hình và kiểm tra hit. Các chỉ báo Jank chính trong iOS: thanh đỏ trên dòng thời gian Core Animation (vượt quá ngân sách khung hình), giá trị Renderer cao (cho thấy kết xuất ngoài màn hình) và FPS thấp. Để giám sát sản xuất, MetricKit thu thập báo cáo với số liệu MXAnimatoryMetric, bao gồm FPS trung bình, thời gian khung hình P50 và P95.

Chẩn đoán Jank gốc trong iOS bao gồm CADisplayLink với kiểm tra timestamp và targetTimestamp. Nếu timestamp hiện tại chậm hơn đáng kể so với targetTimestamp, một hoặc nhiều khung hình đã bị bỏ qua. Apple cũng khuyến nghị sử dụng os_signpost để phân tích tùy chỉnh: đặt signpost-interval ở đầu và cuối quá trình kết xuất khung hình và xem trong Instruments khoảng nào vượt quá 16,6 ms. Trong SwiftUI, UIView.invalidateIntrinsicContentSize được sử dụng để chẩn đoán Jank — các lệnh gọi thường xuyên đến phương thức này cho thấy Layout không ổn định.

CADisplayLink để phát hiện Jank

Mã Swift phát hiện các khung hình bị bỏ qua qua CADisplayLink. Nếu chênh lệch giữa timestamp và targetTimestamp vượt quá 16,6 ms, Jank được ghi lại.

swift
class JankMonitor {

    private var displayLink: CADisplayLink?
    private var totalJank = 0

    func start() {
        displayLink = CADisplayLink(
            target: self,
            selector: #selector(detectJank)
        )
        displayLink?.add(to: .current,
            forMode: .common)
    }

    @objc
    private func detectJank() {
        guard let link = displayLink else { return }
        let delay = link.targetTimestamp
            - link.timestamp
        if delay > 0.0167 {
            totalJank += 1
        }
    }
}

Công cụ phân tích Jank

Để phân tích Jank, cả công cụ tích hợp trong hệ điều hành và SDK bên thứ ba đều được sử dụng. Trên Android, công cụ chính là Perfetto (thay thế Systrace). Perfetto có thể ghi lại dấu vết lên đến 30 giây và phân tích chúng qua giao diện web ui.perfetto.dev. Nó hiển thị dòng thời gian chính xác với hoạt động của Choreographer, luồng kết xuất (RenderThread) và GPU. Để phân tích chi tiết các vấn đề GPU, AGI (Android GPU Inspector) được sử dụng, hiển thị không chỉ thời gian khung hình mà còn tải của các khối GPU cụ thể — shader, bộ rasterization, đơn vị kết cấu.

Trên iOS, công cụ tương đương là Instruments với các mẫu Core Animation, Metal System Trace và GPU Driver. Core Animation hiển thị FPS và thời gian khung hình, Metal System Trace hiển thị công việc GPU đến từng draw call. Để phân tích trên thiết bị thực dưới tải, Firebase Performance (thu thập số liệu Screen Rendering) và Sentry (chụp stack trace khi Jank) được sử dụng. API mới Android 15 Performance Hint cho phép nhà phát triển chỉ cho hệ thống biết khung hình nào quan trọng và nhận cảnh báo từ hệ thống khi Jank đến gần.

FrameMetricsAggregator trong sản xuất

Mã Kotlin sử dụng FrameMetricsAggregator để thu thập thống kê khung hình trong một phiên. Sau khi dừng bộ tổng hợp, số lượng khung hình bị bỏ qua được xuất ra.

kotlin
class JankAggregator(private val activity: Activity) {

    private val aggregator = FrameMetricsAggregator()

    fun startCollection() {
        aggregator.add(activity.window)
    }

    fun stopAndReport() {
        aggregator.remove()
        val result = aggregator.getMetrics()
        val totalFrames = result
            ?.get(FrameMetrics.TOTAL_DURATION)
            ?.size ?: 0
        val jankFrames = result
            ?.get(FrameMetrics.TOTAL_DURATION)
            ?.count { it > 16_666_666L} ?: 0
        Log.d("JankReport",
            "Jank ratio: \${jankFrames * 100 / totalFrames}%")
    }
}

Phương pháp loại bỏ giật khung hình

Loại bỏ Jank đòi hỏi sự kết hợp các kỹ thuật tùy theo loại của nó. Đối với Layout Jank: thay thế hệ thống phân cấp sâu bằng ConstraintLayout/Compose/SwiftUI, sử dụng thẻ merge, tránh requestLayout trong hoạt ảnh. Đối với Draw Jank: sử dụng Debug GPU Overdraw để tìm overdraw 4x+, thay thế drawable nặng bằng đồ họa vector (VectorDrawable/PDF), sử dụng hardware layers một cách thận trọng — chúng tăng tốc kết xuất nhưng tiêu tốn nhiều bộ nhớ GPU hơn. Đối với Thread Jank: di chuyển tất cả các thao tác I/O, công việc cơ sở dữ liệu và giải mã Bitmap sang luồng nền, sử dụng Kotlin Coroutines với Dispatcher phù hợp hoặc RxJava với Schedulers.io().

Đối với GC Jank: giảm thiểu cấp phát trong onDraw() và getView(), sử dụng nhóm đối tượng (ObjectPool), thay thế for-each bằng for có chỉ mục, sử dụng data class bất biến trong Kotlin với copy() một cách thận trọng — copy tạo một đối tượng mới. Đối với IPC Jank: khởi tạo ContentProvider một cách lười biếng qua App Startup, di chuyển các lệnh gọi Binder sang luồng nền. Đối với Rendering Jank: giảm kích thước kết cấu xuống độ phân giải màn hình tối đa, sử dụng nén ASTC hoặc ETC2, tránh biên dịch shader quá mức (biên dịch shader trước). Một giải pháp toàn diện là chạy phân tích Perfetto/Instruments thường xuyên trong CI và theo dõi các suy thoái Jank.

Mẫu Anti-Jank: Async Layout

Mã Kotlin minh họa việc tải dữ liệu không đồng bộ lên màn hình sau reportFullyDrawn, để công việc nặng không chặn khung hình đầu tiên. Callback được gọi sau khi người dùng nhìn thấy giao diện.

kotlin
class JankSafeLoader {

    suspend fun loadAfterFirstFrame(
        activity: Activity
    ) {
        // đảm bảo rằng khung hình đầu tiên đã được kết xuất
        if (Build.VERSION.SDK_INT >= 29) {
            activity.reportFullyDrawn()
        }

        // tải nặng — sau khung hình đầu tiên
        withContext(Dispatchers.IO) {
            val data = fetchHeavyData()
            withContext(Dispatchers.Main) {
                updateUI(data)
            }
        }
    }
}

Câu hỏi thường gặp

Jank trong ứng dụng di động là gì?

Jank là các khung hình kết xuất bị bỏ qua, xuất hiện dưới dạng giật lag hoặc xóc đáng chú ý trong hoạt ảnh. Nó xảy ra khi thời gian chuẩn bị khung hình vượt quá ngân sách thời gian (16,6 ms cho 60 FPS).

Nguyên nhân chính của Jank là gì?

Layout Jank (requestLayout thường xuyên), Draw Jank (overdraw), Thread Jank (chặn luồng chính), GC Jank (thu gom rác), IPC Jank (lệnh gọi Binder) và Rendering Jank (shader nặng).

Làm thế nào để chẩn đoán Jank trong Android?

Sử dụng Perfetto để theo dõi hệ thống, GPU Profiling để phân tích các giai đoạn khung hình và FrameMetricsAggregator để giám sát sản xuất. Trong Android Studio — CPU Profiler với Deep Java Trace.

Làm thế nào để đo Jank trong iOS?

Qua Instruments với mẫu Core Animation hoặc Metal System Trace. Cho sản xuất — MetricKit với MXAnimatoryMetric. Theo cách lập trình — CADisplayLink kiểm tra sự khác biệt giữa timestamp và targetTimestamp.

Tỷ lệ Jank bao nhiêu được coi là nghiêm trọng?

Theo Google, tỷ lệ Jank trên 3% số phiên cuộn (3 trong 100 lần cuộn có giật) dẫn đến tăng 22% đánh giá tiêu cực. Tỷ lệ mục tiêu là dưới 0,5% số phiên cuộn.

Tổng kết

  • Jank — khung hình bị bỏ qua gây ra hiện tượng giật lag có thể thấy trong hoạt ảnh ứng dụng di động.
  • Nguyên nhân chính: Layout Jank, Draw Jank, Thread Jank, GC Jank, IPC Jank và Rendering Jank.
  • Chẩn đoán Jank trên Android — qua Perfetto, GPU Profiling và FrameMetricsAggregator.
  • Chẩn đoán Jank trên iOS — qua Instruments, CADisplayLink và MetricKit.
  • Loại bỏ Jank đòi hỏi sự kết hợp: hệ thống phân cấp phẳng, luồng nền, giảm cấp phát, lưu đệm.
  • Tỷ lệ Jank mục tiêu — dưới 0,5% số phiên cuộn có giật.
  • Phân tích thường xuyên trong CI ngăn chặn suy thoái hiệu suất trước khi chúng đến môi trường sản xuất.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm