Skip to content

Repository files navigation

AppInspect — Sample App

A tiny Android app that exists for one reason: to let you see what AppInspect does, on a real device, in about five minutes.

Clone it, run the debug build, tap a few buttons to generate data, then open the inspector and look at what it captured. Every AppInspect touch point in the code is small and commented, so the app also works as a copy-paste reference for wiring the library into your own project.

AppInspect in one line: a full-screen inspector inside your own debug/internal builds — OkHttp traffic with per-endpoint API analytics, response mocking, SharedPreferences / DataStore / SQLite / Room, WorkManager jobs, crashes and ANRs, a live logcat tail and runtime metadata — with no proxy, no certificate, no desktop client and no adb connection.


1. Requirements

  • Android Studio (any recent version) or just the command line with JDK 17+
  • A device or emulator on Android 7.0 (API 24) or newer
    • ANR and native-crash reporting need Android 11 (API 30)+
    • Notifications need the Android 13+ runtime permission, which the app asks for on launch

2. Clone and run

git clone https://github.com/appinspect/AppInspect-Sample-App.git
cd AppInspect-Sample-App

# build + install the debug variant on a connected device/emulator
./gradlew :app:installDebug

Or open the project in Android Studio, pick the debug variant, and hit Run.

The dependency is resolved from Maven Central, so the first build needs an internet connection.


3. Open the inspector

Any of these work, and none of them need extra host code:

How Notes
Open inspector button Calls AppInspect.open(context)
Shake the device Firm, deliberate shake (default threshold 2.7 g)
Long-press the header card Long-press gesture wired to open()
Launcher shortcut Long-press the app icon on the home screen → Inspect
Tap a network notification Appears after an API call, if the notification permission was granted

The inspector opens as its own task, so it gets a separate card in Recents and can be used in split-screen next to the app.


4. What to try, in order

The demo screen is one list of buttons grouped by the panel they feed.

  1. Network — tap Run 3 calls in a row, then open the inspector → Network. You get request and response headers, bodies, formatted JSON, cURL copy, and .txt / .har export. Unreachable host shows how a transport failure is recorded differently from an HTTP error.
  2. API Analytics — with a few calls captured, tap the Insights icon in the Network panel's top bar. Endpoints are grouped and graded by latency percentiles and error rate, with charts and a ranked list of findings. The sample is frozen when you open it, so percentiles do not shift while you read; new calls surface as a banner. Nothing is computed until you open it, so an unopened panel costs the host app nothing.
  3. Mocks — open Mocks, enable Catalog — empty state, then tap GET catalog again. The response is served from the device and the request never leaves it. The rules come from app/src/debug/assets/appinspect/mocks.json and all ship disabled.
  4. Logcat — tap One line per level, then open Logcat (top bar). Filter by level, tag, text or regex; expand the stack trace from Exception with a cause; toggle Redact after tapping Lines with fake secrets.
  5. Storage — tap Write SharedPreferences and Seed SQLite database, then open Storage. Preferences are editable; the database shows schema and paged rows. The long cached_profile value opens in the full-screen JSON tree viewer.
  6. Background work — tap Enqueue delayed sync job, then open Work. The job sits in ENQUEUED with its constraints and input data visible.
  7. Crashes — tap Crash on main thread. The app dies, which is the point. Relaunch it and open Crashes. Freeze main thread (ANR) works the same way on Android 11+.
  8. Runtime — open it any time (top bar) for app, device and session metadata, including the custom entries this sample registers.

5. Where the library is actually wired

Three places. That is all of it.

File What it does
app/build.gradle.kts debugImplementation(appinspect) + releaseImplementation(appinspect-no-op), and a commented staging build type
SampleApi.kt .addAppInspectInterceptor() — the one line that enables network capture
AppInspectBridge.kt Optional configuration + AppInspect.open()

Everything else in the app (SampleLogs, SampleStorage, SampleWork, SampleCrashes) is ordinary Android code with no reference to AppInspect at all — the panels pick that data up on their own. That is worth noticing: apart from the interceptor, the library observes your app rather than being called by it.

Why there is a "bridge"

AppInspect.open() and updateConfiguration() exist only in the full appinspect artifact. The release build depends on appinspect-no-op, which does not contain them. So those calls live behind one small object with two copies:

  • app/src/debug/java/.../inspector/AppInspectBridge.kt — the real calls
  • app/src/release/java/.../inspector/AppInspectBridge.kt — same signatures, does nothing

The rest of the app is variant-agnostic and the release build compiles cleanly. The OkHttp interceptor is the exception: the no-op artifact ships the same extension as a pass-through, so addAppInspectInterceptor() can sit in src/main unguarded.


6. Build variants

Variant AppInspect artifact Inspector
debug appinspect On. isDebuggable = true → DEBUG tier, everything enabled, nothing to configure
release appinspect-no-op Not present. ~8 KB pass-through interceptor, no UI, no capture, no manifest entries
staging / uat commented out in app/build.gradle.kts See below

AppInspect never sees your Gradle variant names. It resolves a build to a tier from two inputs: FLAG_DEBUGGABLE, and the boolean resource appinspect_enabled_in_non_debuggable_build.

  • debuggable → DEBUG, everything on
  • not debuggable + resource trueSTAGING, everything on
  • not debuggable + resource absent (the default) → PRODUCTION, everything off

So a non-debuggable staging/uat build opts in with one resource line. The commented staging block in app/build.gradle.kts shows the whole thing — including matchingFallbacks, which any custom build type needs because the AppInspect artifacts publish only debug and release variants. This sample keeps it commented so there are just two variants to think about; uncomment it (plus the stagingImplementation line) if you want to try it.

Two independent layers keep the inspector out of production: the release APK contains the no-op artifact, and even the full library disables itself when FLAG_DEBUGGABLE is false. There is no runtime switch that can turn it on in production.


7. Things that surprise people

  • A crash record shows up only after you relaunch. The record is written before the process dies; the panel reads it on the next start.
  • ANRs and native crashes need Android 11+ and are also reported on the next launch.
  • No notifications? The Android 13+ POST_NOTIFICATIONS grant is required. AppInspect declares the permission but has no Activity, so the host app must request it — see MainActivity.
  • A network call is missing. Only clients that added addAppInspectInterceptor() are captured. WebView traffic and other HTTP stacks are not visible.
  • Add the interceptor last on the OkHttp builder. A SHORT_CIRCUIT mock returns without calling proceed(), so anything registered after it is skipped.
  • Debug builds show real secrets — auth headers, tokens, database contents. That is intentional, and it is why exports should be redacted before being attached to a ticket.

8. More


License

Apache License 2.0 — see LICENSE. The wiring code in this sample is intended to be copy-pasted into your own project.

About

AppInspect is a Gradle dependency that adds a full-screen inspector to your own debug builds. Open it on the device to read the calls your app just made, replace any response on the spot, and check storage, background jobs, crashes and the live log — no proxy, no cable, and nothing to configure.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages