androidengineers.Book a session

Monitoring & Analytics

Performance Monitoring & Traces

article20 minMedium

Performance monitoring lets you measure what matters — startup time, screen load time, network latency — in the field across millions of real devices and network conditions.

Firebase Performance Monitoring

Automatic Instrumentation

Firebase Performance automatically measures:

  • App start time (cold/warm/hot)
  • HTTP/S network requests (latency, payload size, success rate)
  • Foreground/background session duration

Custom Traces

Measure any specific operation:

// Simple trace
val trace = Firebase.performance.newTrace("article_render")
trace.start()

// ... load and render article
val article = repository.getArticle(id)
renderArticle(article)

trace.stop()

// With custom metrics and attributes
val trace = Firebase.performance.newTrace("search_query")
trace.putAttribute("query_length", query.length.toString())
trace.start()

val results = search(query)

trace.putMetric("result_count", results.size.toLong())
trace.stop()

Measuring Screen Load Time

class ArticleDetailViewModel @Inject constructor(
    private val repository: ArticleRepository,
    private val perf: FirebasePerformance
) : ViewModel() {

    private var loadTrace: Trace? = null

    fun startLoad() {
        loadTrace = perf.newTrace("article_detail_load").also { it.start() }
    }

    fun loadArticle(id: String) = viewModelScope.launch {
        startLoad()
        try {
            val article = repository.getArticle(id)
            _state.value = UiState.Success(article)
            loadTrace?.putAttribute("source", "network")
        } catch (e: Exception) {
            loadTrace?.putAttribute("error", e.javaClass.simpleName)
            _state.value = UiState.Error(e.message ?: "")
        } finally {
            loadTrace?.stop()
        }
    }
}

HTTP Monitoring Override

// OkHttp interceptor for custom performance monitoring
class PerformanceInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val metric = Firebase.performance.newHttpMetric(
            request.url.toString(),
            FirebasePerformance.HttpMethod.GET
        )
        metric.start()

        val response = chain.proceed(request)

        metric.setHttpResponseCode(response.code)
        metric.setResponsePayloadSize(response.body?.contentLength() ?: -1)
        metric.stop()

        return response
    }
}

Systrace / Perfetto: Deep Analysis

For frame-level analysis that Firebase can't provide:

# Record a Perfetto trace while reproducing a jank issue
adb shell perfetto \
  -o /data/misc/perfetto-traces/trace.perfetto-trace \
  -t 10s \
  "track_event,view,gfx,input,sched,power"

# Pull the trace
adb pull /data/misc/perfetto-traces/trace.perfetto-trace
# Open at ui.perfetto.dev

Android StrictMode: Catch Issues in Development

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .penaltyDeath()   // crash in DEBUG so you can't miss it
                    .build()
            )
            StrictMode.setVmPolicy(
                StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .detectLeakedClosableObjects()
                    .detectActivityLeaks()
                    .penaltyLog()
                    .build()
            )
        }
    }
}

App Startup Tracing (Jetpack App Startup)

// Measure initialization order and duration
class AnalyticsInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        val trace = Firebase.performance.newTrace("analytics_init")
        trace.start()
        Analytics.initialize(context)
        trace.stop()
    }
    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

Rendering Performance

// Compose: measure recomposition count in debug builds
@Composable
fun ArticleList(articles: List<Article>) {
    RecompositionCounter("ArticleList")  // custom debug composable
    LazyColumn { /* ... */ }
}

// Report slow frame rate to analytics
class FrameRateMonitor(private val analytics: Analytics) {
    fun onFrameRendered(durationMs: Long) {
        if (durationMs > 16) {   // > 60fps threshold
            analytics.logEvent("slow_frame") {
                param("duration_ms", durationMs)
                param("screen", currentScreen)
            }
        }
    }
}

Key Takeaways

ToolWhat it measuresWhen to use
Firebase PerformanceApp start, network, custom tracesProduction-wide field data
Android VitalsCrash rate, ANR, slow rendersPlay Console; affects store ranking
StrictModeDisk/network on main thread, leaksDebug builds only
Perfetto/SystraceFrame-level janks, scheduler eventsReproducing specific jank bugs
Custom tracesYour specific flows (checkout, search)Measure what matters to your business

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Performance Monitoring & Traces | Android System Design | Android Engineers