Jank — interfeys animasiyasında nəzərə çarpan gecikmələr və ya “tutulmalar” mənasını verən termindir, ayrı-ayrı kadrların buraxılması nəticəsində yaranır. Mobil tətbiqlərdə Jank, kadrın render vaxtı displeyin yenilənmə tezliyi ilə ayrılmış büdcəni keçdikdə baş verir. Android Developers, 2025-ə görə, Jank subyektiv “yavaşlama” hissinin əsas səbəbidir — tətbiq funksional olaraq mükəmməl ola bilər, lakin istifadəçi onu qeyri-sabit FPS səbəbindən yavaş qəbul edir.
Əsas məqamlar
Jank — kompüter qrafikası sahəsində animasiyanın hamar sürüşmə əvəzinə sıçrayışlarla hərəkət etdiyi vizual qüsur mənasını verən termindir. Mobil inkişafda Jank vaxt vahidi üçün buraxılmış kadrların sayı (skipped frames) ilə ölçülür. Sistem VSync anına qədər kadr hazırlamağa vaxt tapmazsa, displey əvvəlki kadrı təkrarlayır — 60 Hz-də 16.6 ms davam edən pauza yaranır. Bir buraxılmış kadr nəzərə çarpmaya bilər, lakin ardıcıl 3–5 buraxılmış kadr seriyası istifadəçinin aydın hiss etdiyi 50–80 ms davam edən “yavaşlama” hissi yaradır.
Jank xüsusilə sabit sürətlə işləməli olan animasiyalar üçün kritikdir: lentin sürüşdürülməsi, menyu açma animasiyası, parallaks effektləri, ekranlar arası keçidlər. Google-un UX tədqiqatına (2024) görə, Jank göstəricisi sürüşdürmə sessiyalarının 3%-dən çox olan tətbiq, göstəricisi 0.5%-dən az olan tətbiqə nisbətən 22% daha çox birulduzlu rəy alır. Android Vitals aləti avtomatik olaraq Jank-ı izləyir və severity-ə görə təsnifləşdirir: moderate, severe və critical.
Jank-ın səbəbləri bir neçə kateqoriyaya bölünür. Birinci — Layout Jank: View ölçülərinin dəyişməsi, LayoutTransition animasiyaları və ya dinamik məzmun yüklənməsi səbəbindən tez-tez requestLayout() çağırışları nəticəsində yaranır. Hər requestLayout çağırışı bütün View alt ağacı üçün Measure + Layout işə salır, bu da 5–30 ms çəkə bilər. İkinci — Draw Jank: həddindən artıq çəkmə (overdraw) və ağır drawable-ların istifadəsi ilə əlaqədardır. Üçüncü — Thread Jank: sinxron əməliyyatlar — fayl yükləmə, əsas axında verilənlər bazası ilə iş, Bitmap dekodlaşdırma səbəbindən main thread-ın bloklanması.
Dördüncü kateqoriya — GC Jank: ART/Dalvik və ya Swift ARC-də garbage collection. Yığında çoxlu obyekt toplandıqda, GC 5–15 ms davam edən Stop-The-World fasiləsi işə salır. Android-də GC fasilələri ən çox dövrlərdə tez-tez alokasiyalar zamanı baş verir: onDraw()-da obyekt yaratma, adapterlərdə alokasiyalar, istifadə olunmayan lambda ifadələri. Beşinci — IPC Jank: əsas axında proseslərarası qarşılıqlı əlaqə (ContentProvider, Binder). Altıncı — Rendering Jank: qeyri-optimal şeyderlər və ya böyük ölçülü teksturalar səbəbindən yavaş GPU renderi.
| Jank növü | Səbəb | Tipik müddətAxtarış aləti | |
|---|---|---|---|
| Layout | requestLayout, relayout | 5–30 ms | Perfetto, Systrace |
| Draw | Overdraw, ağır drawable | 3–20 ms | GPU Profiling |
| Thread | Main thread bloklanması | 10–200 ms | Android Studio Profiler |
| GC | Garbage Collection | 5–15 ms | Memory Profiler |
| Rendering | GPU yüklənməsi | 10–50 ms | GPU Tracer, Xcode GPU |
Android-də Jank diaqnostikası sistem treysi Perfetto ilə başlayır. Perfetto bütün axınların, CPU, GPU və planlayıcının fəaliyyətini qeyd edir. Jank-ın aydın göstəricisi Choreographer.doFrame və Choreographer.doCallbacks sətirləridir: iki ardıcıl doFrame çağırışı arasında interval 16.6 ms-dən çox olarsa, kadr buraxılmışdır. Perfetto dəqiq səbəbi göstərir — hansı system call, lock və ya GC gecikməyə səbəb oldu. Android Studio Profiler-də oxşar funksionallıq CPU Profiler vasitəsilə mövcuddur.
Produksiyada Jank-ın avtomatik aşkarlanması üçün FrameMetricsAggregator istifadə olunur — hər kadr üzrə statistika toplayan və sessiya üzrə aqreqasiya edən API. Android 12+-də PerformanceHintManager meydana çıxdı — sistemə hədəf kadr tezliyi haqqında göstəriş vermək üçün API. Tətbiq 120 FPS ssenarisində işlədiyini göstərərsə, sistem Jank-ın qarşısını almaq üçün CPU/GPU tezliyini artıra bilər. Bütün buraxılmış kadrların sadə loglanması üçün Choreographer.FrameCallback-ə abunə olmaq kifayətdir.
Kotlin kodu Choreographer.FrameCallback-ə abunə olur və gecikmə müddətini göstərərək hər buraxılmış kadrı loglayır. Callback hər VSync-də çağırılır.
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",
"$skippedFrames kadr buraxıldı")
}
}
previousFrameTime = currentTime
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(callback)
}
}
iOS-da Jank diaqnostikası Core Animation şablonu ilə Instruments vasitəsilə aparılır. Instruments real vaxtda FPS, offscreen-renderlərin sayı və hit-testləri göstərir. iOS-da Jank-ın əsas göstəriciləri: Core Animation vaxt miqyasında qırmızı sütunlar (kadr büdcəsinin aşılması), yüksək Renderer göstəricisi (offscreen rendering mənasını verir) və aşağı FPS. Produksiya monitorinqi üçün MetricKit orta FPS, P50 və P95 kadr vaxtını əhatə edən MXAnimatoryMetric metrikası ilə hesabatlar toplayır.
iOS-da Jank-ın yerli diaqnostikası timestamp və targetTimestamp yoxlanması ilə CADisplayLink daxildir. Cari timestamp targetTimestamp-dan əhəmiyyətli dərəcədə geri qalırsa, bir və ya bir neçə kadr buraxılmış deməkdir. Apple həmçinin xüsusi profil üçün os_signpost istifadə etməyi tövsiyə edir: kadr renderinin əvvəlində və sonunda signpost-interval yerləşdirmək və Instruments-da hansı intervalların 16.6 ms-dən çox olduğuna baxmaq. SwiftUI-də Jank diaqnostikası üçün UIView.invalidateIntrinsicContentSize istifadə olunur — bu metodun tez-tez çağırılması qeyri-sabit Layout göstərir.
Swift kodu CADisplayLink vasitəsilə buraxılmış kadrları müəyyən edir. Timestamp ilə targetTimestamp arasındakı fərq 16.6 ms-dən çox olarsa — Jank qeydə alınır.
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
}
}
}
Jank profilinqi üçün həm əməliyyat sisteminin quraşdırılmış alətlərindən, həm də üçüncü tərəf SDK-lardan istifadə olunur. Android-də əsas alət Perfetto-dur (Systrace-i əvəz etdi). Perfetto 30 saniyəyə qədər treysləri qeyd etməyə və onları ui.perfetto.dev veb interfeysi vasitəsilə təhlil etməyə imkan verir. Choreographer, render axınları (RenderThread) və GPU ilə işin dəqiq zaman miqyasını göstərir. GPU ilə bağlı problemlərin ətraflı təhlili üçün AGI (Android GPU Inspector) istifadə olunur, yalnız kadr vaxtını deyil, həm də GPU-nun konkret bloklarının — şeyderlərin, rastrlaşdırıcının, tekstur blokunun yüklənməsini göstərir.
iOS-da analoq — Instruments Core Animation, Metal System Trace və GPU Driver şablonları ilə. Core Animation FPS və kadr vaxtını, Metal System Trace — hər draw call-a qədər dəqiqliklə GPU işini göstərir. Yük altında real cihazlarda profilinq üçün Firebase Performance (Screen Rendering metrikası toplayır) və Sentry (Jank zamanı stack trace ələ keçirir) istifadə olunur. Yeni Android 15 Performance Hint API-i tərtibatçıya sistemə hansı kadrların vacib olduğunu göstərməyə və Jank-a yaxınlaşdıqda sistemdən xəbərdarlıqlar almağa imkan verir.
Kotlin kodu sessiya üzrə kadr statistikası toplamaq üçün FrameMetricsAggregator istifadə edir. Aqreqator dayandırıldıqdan sonra buraxılmış kadrların sayı göstərilir.
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 nisbəti: ${jankFrames * 100 / totalFrames}%")
}
}
Jank-ın aradan qaldırılması onun növündən asılı olaraq metodların kombinasiyasını tələb edir. Layout Jank üçün: dərin iyerarxiyaları ConstraintLayout/Compose/SwiftUI ilə əvəz etmək, merge-teqləri istifadə etmək, animasiyalarda requestLayout-dan qaçmaq. Draw Jank üçün: 4x+ overdraw tapmaq üçün Debug GPU Overdraw istifadə etmək, ağır drawable-ları vektor (VectorDrawable/PDF) ilə əvəz etmək, hardware layers-i ehtiyatla istifadə etmək — onlar renderi sürətləndirir, lakin daha çox GPU yaddaşı sərf edir. Thread Jank üçün: bütün I/O əməliyyatlarını, verilənlər bazası ilə işi və Bitmap dekodlaşdırmanı fon axınlarına köçürmək, düzgün Dispatcher ilə Kotlin Coroutines və ya Schedulers.io() ilə RxJava istifadə etmək.
GC Jank üçün: onDraw() və getView()-də alokasiyaları minimuma endirmək, obyekt hovuzlarından (ObjectPool) istifadə etmək, for-each-i indeksli for ilə əvəz etmək, Kotlin-də immutable data class və copy()-dən ehtiyatla istifadə etmək — copy yeni obyekt yaradır. IPC Jank üçün: ContentProvider-i App Startup vasitəsilə tənbəl initallaşdırmaq, Binder çağırışlarını fon axınına köçürmək. Rendering Jank üçün: tekstur ölçüsünü ekranın maksimum rezolyusiyasına qədər azaltmaq, ASTC və ya ETC2 sıxlaşdırması istifadə etmək, lazımsız shader compilation-dan qaçmaq (şeyderləri əvvəlcədən kompilyasiya etmək). Kompleks həll — CI-da müntəzəm Perfetto/Instruments profilinqi işə salmaq və Jank reqressiyalarını izləmək.
Kotlin kodu reportFullyDrawn-dan sonra ekrana asinxron məlumat yükləməni nümayiş etdirir ki, ağır iş ilk kadrı bloklamasın. Callback istifadəçi interfeysi gördükdən sonra çağırılır.
class JankSafeLoader {
suspend fun loadAfterFirstFrame(
activity: Activity
) {
// zəmanət veririk ki, ilk kadr artıq çəkilib
if (Build.VERSION.SDK_INT >= 29) {
activity.reportFullyDrawn()
}
// ağır yükləmə — ilk kadrdan sonra
withContext(Dispatchers.IO) {
val data = fetchHeavyData()
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
}
Tez-tez verilən suallar
Jank — render edilmiş kadrların nəzərə çarpan gecikmələr və ya animasiya sıçrayışları kimi özünü göstərən buraxılmış kadrlardır. Kadr hazırlama vaxtı vaxt büdcəsini (60 FPS üçün 16.6 ms) aşdıqda yaranır.
Layout Jank (tez-tez requestLayout), Draw Jank (overdraw), Thread Jank (main thread bloklanması), GC Jank (garbage collection), IPC Jank (Binder çağırışları) və Rendering Jank (ağır şeyderlər).
Sistem treysi üçün Perfetto, kadr fazalarının təhlili üçün GPU Profiling və produksiya monitorinqi üçün FrameMetricsAggregator istifadə edin. Android Studio-da — CPU Profiler Deep Java Trace ilə.
Core Animation və ya Metal System Trace şablonu ilə Instruments vasitəsilə. Produksiya üçün — MXAnimatoryMetric ilə MetricKit. Proqram olaraq — timestamp və targetTimestamp fərqini yoxlamaqla CADisplayLink.
Google məlumatlarına görə, sürüşdürmə sessiyalarının 3%-dən çox Jank göstəricisi (100 sürüşdürmədən 3-ü gecikmə ehtiva edir) mənfi rəylərin 22% artmasına səbəb olur. Hədəf göstərici — sürüşdürmə sessiyalarının 0.5%-dən azı.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun