AppInspect
On this page
Getting started

Install

Two dependency lines get you a working inspector. A third line on your OkHttpClient turns on network capture. Everything after that is optional.

Before you start

  • minSdk 24 (Android 7.0). ANR and native crash reporting additionally need Android 11, because they read the OS ApplicationExitInfo API.
  • OkHttp, if you want the Network and Mocks panels. Storage, WorkManager, Crashes and Runtime work without it.
  • Build variants you can target separately — at minimum debug and release. If you have a staging or internal-test variant, so much the better.

Step 1 — resolve from Maven Central

AppInspect is published to Maven Central, so in most projects this is already in place. If your project declares repositories centrally, confirm mavenCentral() is there.

settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

Step 2 — add the dependency per variant

Add the full library to the builds you test, and the no-op stub to the build you ship. Do not use plain implementation — that puts the inspector in your production APK.

app/build.gradle.kts
dependencies {
    val appInspect = "io.github.suryansh1720001.appinspect"

    // The full inspector, for builds your team tests
    debugImplementation("$appInspect:appinspect:<latest-version>")

    // A pass-through stub, so shared OkHttp code still compiles in release
    releaseImplementation("$appInspect:appinspect-no-op:<latest-version>")
}

That is enough to have a working inspector. AppInspect initialises itself through AndroidX Startup when the process starts, so there is nothing to add to your Application class for the default setup.

If you have more than two variants

Gradle only generates debugImplementation and releaseImplementation automatically. For your own variants, add the configuration by name:

app/build.gradle.kts
dependencies {
    val group = "io.github.suryansh1720001.appinspect"
    val inspector = "$group:appinspect:<latest-version>"

    debugImplementation(inspector)
    add("stagingImplementation", inspector)
    add("internalTestImplementation", inspector)

    releaseImplementation("$group:appinspect-no-op:<latest-version>")
}
A staging build is not automatically enabled

Adding the artifact to a variant is not the same as letting it run there. The library also checks the build tier at runtime, and anything that is not debuggable stays off until you opt in explicitly. See enablement if your staging build is signed as a release build.

Step 3 — capture network traffic

Network capture is opt-in, because AppInspect never touches an OkHttpClient you did not hand to it. Add the extension to your builder, and add it last:

Your OkHttp setup
val client = OkHttpClient.Builder()
    .addInterceptor(authInterceptor)
    .addInterceptor(loggingInterceptor)
    .addAppInspectInterceptor()   // last
    .build()

The same code compiles in every variant. In release the extension comes from appinspect-no-op and returns the builder untouched, so there is no if (BuildConfig.DEBUG) to write.

Why the order matters

Two reasons, and both are easy to get wrong.

Short-circuit mocks skip whatever comes after. When a SHORT_CIRCUIT rule fires, AppInspect returns a response without calling chain.proceed(), so every interceptor registered after it never runs. If your token-refresh interceptor sits after AppInspect, mocking will quietly bypass it. Last means nothing of yours gets skipped.

Last is also where the request looks the way it really goes out — after your own interceptors have added their headers.

You get the real wire headers

addAppInspectInterceptor() actually installs two interceptors: an application-level one that does the capturing and the mocking, and AppInspectOkHttpWireHeaderInterceptor at the network level, which only observes.

The second one exists because an application interceptor cannot see headers added downstream of itself — an Authenticator retry, another network interceptor, or OkHttp's own BridgeInterceptor, which adds Host, User-Agent, Accept-Encoding, Content-Length, Content-Type and Cookie. Without it, an Authorization header attached later in the chain would simply be missing from the panel. With it, what you see matches what Android Studio's Network Inspector shows.

Where wire headers genuinely do not exist — a short-circuited mock, a fully cached response, a call that failed before reaching the network — the event falls back to the application-level headers. This also means captured request headers can contain credentials your calling code never set, which matters when you share an export: see the QA checklist.

Step 4 — check that it worked

  1. Install and launch a debug build.
  2. Shake the device firmly. The inspector should open full-screen. If shaking feels unreliable on your device, other entry points work just as well.
  3. Trigger a network call in your app, then open the Network panel. The call should be listed with its method, path, status and duration.

If the inspector opens but Network is empty, the interceptor is not on the client that made the call — a second OkHttpClient built somewhere else is the usual culprit. If nothing opens at all, the build is probably not debuggable; check enablement.

Optional — take over the setup yourself

Auto-initialisation uses the default configuration. Call AppInspect.install() from your Application instead when you want to change something — which panels appear, what testers can edit, which build tiers are allowed.

DebugApplication.kt (debug source set)
AppInspect.install(
    application = this,
    configuration = AppInspectConfiguration(
        entryPoints = AppInspectEntryPoints(shakeToOpenEnabled = true),
        panels = AppInspectPanels(mocksEnabled = true),
    ),
)

Every field is documented on the configuration reference page. AppInspect.initialize(configuration) still works and is kept for compatibility, but prefer install().

One thing to guard by variant

appinspect-no-op deliberately mirrors only the OkHttp API surface. AppInspect.install(), AppInspect.open() and the long-press trigger helpers do not exist there, so calling them from shared code breaks the release build.

Keep those calls in a debug-only source set, or behind your own no-op indirection. The interceptor line is the only part designed to live in shared code.

Notifications

AppInspect can post a notification for each captured call, which is handy for watching traffic without keeping the inspector open. On Android 13 and newer your app has to request the runtime permission itself — see Network notifications.