Java performance audit
Skill almasumdev/awesome-java-android-agent-skills/.github/skills/performance/java-performance-audit
Expert guidance on auditing a Java Android app's runtime performance with Android Studio Profiler, StrictMode, Layout Inspector, and GPU overdraw. Use this when investigating jank, slow startup, ANRs, or memory leaks.From its SKILL.md
npx -y skills add almasumdev/awesome-java-android-agent-skills --skill java-performance-auditAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.1 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it
Java Android Runtime Performance Audit
Instructions
Performance work is measurement-first. Do not optimize without a trace.
1. Establish a Baseline
Measure these before changing any code:
- Startup time —
adb shell am start -W ...→TotalTime,WaitTime. - Frame rate — Profiler → CPU → System Trace, or Macrobenchmark library.
- Memory — Profiler → Memory → record allocations.
- Network — Profiler → Network timeline.
Save traces in docs/perf/ with a date; every future change compares against the baseline.
2. StrictMode — catch main-thread IO immediately
Enable StrictMode in debug builds only:
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyFlashScreen()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedClosableObjects()
.detectLeakedSqlLiteObjects()
.detectActivityLeaks()
.penaltyLog()
.build());
}
}
}
Treat every StrictMode log line as a bug to file.
3. Layout Inspector & Hierarchy
- Aim for depth < 8 from the Activity's root. Anything deeper pays for a measure pass per level.
- Flatten nested
LinearLayouts intoConstraintLayout(seeandroid-views-xmlskill). RelativeLayoutdoes two measure passes — preferConstraintLayout.- Remove
android:backgroundset to the same color as the parent; it causes overdraw.
Enable Debug GPU Overdraw (developer options) — any red regions on a scrollable list are the main cost to fix.
4. RecyclerView
- Use
DiffUtil/ListAdapter; never callnotifyDataSetChanged()for content changes. - Set
setHasStableIds(true)+getItemId(position)when IDs are stable. - Use view pools across RecyclerViews showing the same cell type.
- Set fixed sizes on
ImageViews so Glide can downsample (seeglide-imagesskill). - Binding should do zero allocations on the hot path — move formatters to fields, pre-compute
contentDescription.
5. Startup
- Move work out of
Application.onCreate— use Jetpack App Startup (androidx.startup) to lazy-initialize libraries. - Use
Baseline Profiles(androidx.benchmark:benchmark-macro-junit4) to generate a profile for R8 to pre-JIT critical paths. - Defer WorkManager enqueues behind
DelayableInitializer.
public class CrashReporterInitializer implements Initializer<Unit> {
@NonNull
@Override public Unit create(@NonNull Context context) {
CrashReporter.init(context);
return Unit.INSTANCE;
}
@NonNull
@Override public List<Class<? extends Initializer<?>>> dependencies() {
return List.of();
}
}
6. Memory
- Profiler → Memory → "Dump Java heap" → filter by
Activity. If an Activity instance persists afterfinish(), you have a leak. - Use LeakCanary (
com.squareup.leakcanary:leakcanary-android) indebugImplementation. - Common leaks:
- Inner classes holding implicit outer reference (Fragment listeners, Handlers).
- Singletons that cache
ActivityorView. - Forgotten
BroadcastReceiver/Observerregistrations.
7. ANRs
An ANR is any unresponsive main-thread window > 5s. Diagnose with:
adb bugreport→traces.txt.- Firebase Crashlytics ANR collection.
- Google Play vitals.
Root causes map 1:1 to what StrictMode catches: disk IO, network, large layouts, broadcast processing on main thread.
8. Background Work
- Use
JobScheduler/WorkManagerinstead ofBOOT_COMPLETEDreceivers. - Coalesce network calls; prefer
OkHttpconnection pool reuse (oneOkHttpClientper app). - Batch Room writes in a transaction:
db.runInTransaction(() -> { ... }).
9. Profiling Cadence
- Run Systrace on release builds (AOT-compiled) — debug builds with R8 off are not representative.
- Test on a mid-tier device (Pixel 6a, Samsung A-series), not a flagship.
- Record a 30-second trace of the primary user journey each release.
Checklist
- StrictMode is enabled in debug and produces zero violations during the smoke journey.
-
Profile GPU Renderingshows no frames above the 16ms line on the main list screen. - View hierarchy depth is under 8 for every primary screen.
-
notifyDataSetChanged()does not appear in production code. - LeakCanary is integrated in debug and the primary journey produces no leaks.
- Cold startup
TotalTimeis tracked release-over-release and trending flat or down. - App Startup library is used; heavy libraries initialize lazily.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.