Hackar — det är användarens beskrivning av situationen när en mobilapp fungerar långsamt och ostadigt: ibland svarar den normalt, ibland fryser den plötsligt i flera sekunder. I teknisk kontext betyder "hackar" en kombination av lag och mikro-frysningar orsakade av frekventa GC-pauser, blockering av huvudtråden av synkrona operationer och icke-optimala datastrukturer. Enligt Android Performance Benchmarking Guide ökar minskningen av svarstiden från 300 ms till 100 ms användarbehållningen med 25%. Diagnostik av hackande kräver en kombination av CPU- och Memory-profilering med analys av frekvensen av skräpinsamling.
Huvudpunkter
Hackar — en informell term som användare använder för att beskriva subjektivt långsam app-prestanda. Till skillnad från lag, som manifesterar sig som en konstant fördröjning, är hackande oregelbundna frysningar: appen kan fungera perfekt i några sekunder och sedan "tänka" i 1–3 sekunder.
Ur profileringssynpunkt manifesterar sig hackande som en serie missade bildrutor (jank) med toppfördröjningar över 100 ms. På FPS-grafen ser detta ut som kraftiga fall: 60 → 20 → 55 → 10 bildrutor per sekund. Till skillnad från lag med jämnt låg FPS har hackande tydlig variabilitet.
När appen hackar förstår användaren inte logiken bakom inbromsningarna: skärmen kan scrolla smidigt och sedan plötsligt stanna i en sekund. Detta orsakar frustration och minskar förtroendet för appen. Enligt Google lämnar 53% av användarna en webbplats eller app om laddningen tar längre tid än 3 sekunder.
Den oregelbundna karaktären av hackande indikerar att problemet orsakas av händelsefaktorer, inte konstant överbelastning. Låt oss titta på typiska scenarier.
På Android i ART-miljön stoppar skräpinsamling alla appens trådar. Om många tillfälliga objekt skapas i koden — till exempel vid varje anrop av onBindViewHolder skapas en ny String genom konkatenation — startar GC oftare. Pausen kan vara 5–50 ms beroende på heap-storlek och objektgeneration. Användaren upplever detta som plötsligt "tänkande".
Room på Android och Core Data på iOS stöder asynkrona frågor, men utvecklare anropar ofta getValue() eller utför frågor via runBlocking för enkelhetens skull. En tung SELECT med joins på en tabell med 10 000 rader kan ta 200–500 ms och helt blockera UI under den tiden.
Att ladda en kamerabild (12 Mp, 4000x3000 px) utan skalning tar upp till 200 ms att avkoda till Bitmap. Om bilder laddas asynkront men utan en begränsad trådpool kan samtidig start av 5–6 avkodningar överbelasta CPU, vilket orsakar migrerande inbromsningar.
Diagnostik av oregelbundna inbromsningar är svårare än diagnostik av konstanta lag, eftersom problemet kanske inte uppträder vid varje start. Insamling av statistik under en längre period krävs.
Android Studio Memory Profiler visar inte bara minnesanvändning utan även GC-händelser: frekvens, typ (Concurrent, Full), varaktighet. Om GC inträffar mer än 1 gång per 5 sekunder i vilotillstånd — detta är ett tecken på överdriven allokering. Inspelning av heap dump vid hackningsögonblicket gör det möjligt att se vilka objekt som upptar minne.
På iOS använder du mallen Allocations i Instruments för att spåra skapande och frigörande av objekt. Aktivera generationer (Generations) — de gör det möjligt att ta ögonblicksbilder av heap mellan åtgärder och se vilka objekt som finns kvar i minnet. Beständiga objekt som inte frigörs — källa till minnesackumulering och efterföljande pauser.
JankStats — Android-bibliotek som samlar in metriker för missade bildrutor i realtid. Den knyter varje jank till det aktuella scenariot (t.ex. "listrullning", "skärmöppning"), vilket gör det möjligt att förstå vid vilken specifik åtgärd hackandet uppstår.
Exempel på JankStats-integration för att spåra frysningar på Android:
class MainActivity : AppCompatActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
jankStats = JankStats.create(this.window.decorView) { frameData ->
if (frameData.isJank()) {
Log.w("Jank", "Duration=${frameData.durationMs}ms")
}
}
}
}
Att eliminera hackande kräver riktat arbete med varje orsak. Det finns ingen universallösning — analys av specifika prestandaprofiler krävs.
Om listan innehåller 1000+ element och alla laddas på en gång — detta är garanterat hackande. Paging 3 på Android och NSFetchedResultsController på iOS laddar data i portioner under rullning. Användaren ser bara de första 10–20 elementen, resten laddas i bakgrunden.
Room tillåter profilering av frågor via Inspection Tool i Android Studio: exekveringstid, antal returnerade rader och frågeplan syns. Att lägga till index på WHERE- och ORDER BY-kolumner kan minska frågetiden från 300 ms till 5 ms. På iOS utförs en liknande kontroll av Core Data Profiler i Instruments.
Bakgrundssynkroniseringar, filuppladdningar, databearbetning — allt detta bör utföras via WorkManager (Android) eller Background Tasks (iOS). Om synkronisering startas i UI-tråden kommer appen att hacka under exekvering. WorkManager garanterar exekvering i bakgrundstråden med hänsyn till batteri- och nätverksstatus.
Exempel på bakgrundssynkronisering via WorkManager på Android:
class SyncWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
Log.d("Sync", "Synkronisera data i bakgrundstråd")
syncData()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
Hackande kan förebyggas i kodningsfasen genom att följa principerna för effektivt arbete med minne och trådar.
Baseline Profiles — lista över klasser och metoder som Android kompilerar i förväg (AOT), inte JIT. Utan profil kompileras varje ny skärm vid första öppningen, vilket orsakar en fördröjning på 100–500 ms. Förbered Baseline Profile för viktiga skärmar och aktivera generering i Gradle via baseline-profile-gradle-plugin.
Hot path — kod som exekveras vid varje bildruta: onBindViewHolder, draw, layoutSubviews. Undvik att skapa objekt i dessa metoder: använd en objektpool, StringBuilder istället för konkatenation, cache:a formaterade strängar och formaterare. Varje extra allokering närmar nästa GC.
Lägg till i CI-pipelinen att köra Macrobenchmark med scenario för listrullning och skärmöppning. Sätt ett tröskelvärde: 99:e percentilen av bildrutetiden får inte överstiga 16 ms. Om tröskeln överskrids — bygget avvisas tills optimering.
Vanliga frågor
Lag — konstant fördröjning (t.ex. 200 ms per tryck). Hackar — oregelbundet: appen fungerar normalt, saktar sedan plötsligt ner i 1–3 sekunder, sedan normalt igen. Orsak — händelsefaktorer som GC-pauser eller synkrona databasfrågor.
Använd Memory Profiler i Android Studio: fliken Memory visar GC-händelser med varaktighet. För produktionsövervakning, anslut Firebase Performance Monitoring med anpassade spårningar. På iOS aktivera Malloc Debug och markera allokeringsgenerationer i Instruments.
Indirekt — ja. Om serverns svar kommer med fördröjning och UI väntar synkront på det, fryser appen. Om förfrågan är asynkron men bearbetningen av svaret sker i UI-tråden — detta kommer också att orsaka hackande. Lösning — asynkron bearbetning med korutiner och förloppsindikatorer.
Vid felaktig användning kan KMP generera överflödiga wrapper-objekt för interoperabilitet. På iOS ökar detta allokeringsfrekvensen och följaktligen ARC-pauser. Använd @ObjCName, optimera expect/actual och undvik frekventa anrop av delad kod från heta vägar i UI.
Att öka heap via android:largeHeap="true" fördröjer GC men eliminerar inte orsaken till allokeringar. När GC slutligen startar blir pausen längre eftersom fler objekt måste gås igenom. Lösning — minska antalet allokeringar, utöka inte heapen.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också