See what your app is really doing — from inside the app.
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.
- onboarding_done
- true
- user_tier
- "gold"
- last_sync_at
- 1755712493
- cached_profile
- {…} 214 KB
- retry_count
- 3
11:55:13.882 OkHttp HTTP FAILED: SocketTimeoutException
11:55:13.884 OrdersRepo load failed — tap to expand 24 frames
11:55:11.207 WM-Worker UploadReceiptWorker retry 4 of 5
11:55:09.940 OkHttp --> GET /api/v1/payments
11:55:09.941 AppInspect mock served: payments-down
11:55:07.512 AuthStore token refreshed, exp in 3600s
11:55:02.118 Choreographer skipped 41 frames
App
- Version
- 4.18.2 (41802)
- Build type
- debug
- Target / min SDK
- 35 / 24
Environment
- Backend
- qa-2
- Flag cohort
- internal
Device
- Model
- Pixel 8a
- Android
- 15 (API 35)
- Memory free
- 2.1 / 8 GB
- Locale · dark
- en-IN · on
Session
- Started
- 11:48:07
- Session id
- a91f…4c2
Every OkHttp call, with filters, full-text search and export as cURL, TXT or HAR. Network docs →
Three things happen, and none of them involve your laptop.
Most Android debugging tools ask you to set something up outside the app — a desktop proxy, a certificate, a cable, a separate desktop client. AppInspect is a library, so the tool ships with the build the tester already has.
You add a dependency
One line for debug builds, one for release. AppInspect starts itself through
AndroidX Startup and every feature is on by default, so there is no
configuration to write and nothing to add to your
Application class.
Anyone opens it on the device
Shake the phone, tap a launcher shortcut, long-press a view, tap a network
notification, or call AppInspect.open(context) from your own
debug menu.
Evidence leaves as a file
A cURL command, a .txt log, a HAR file for DevTools, a crash
report, or a set of mock rules — through the normal Android share sheet,
straight into the ticket.
Eight surfaces, one for each question a bug tends to raise.
Five sit on the bottom bar; Runtime and Logcat are one tap away in the top bar, and any of them can hand an oversized value to the shared viewer. Each has its own page in the docs, because knowing a panel exists is not the same as knowing how it behaves when the payload is 200 KB, the file is encrypted, or the list comes back empty.
Network
Every OkHttp call with filters and full-text search, request headers read off the wire so downstream credentials still show up, and export as cURL, TXT, or HAR 1.2.
Network panel → MMocks
Replace a live response with your own status, headers, body, delay, or a
simulated timeout. Build a rule from a call you just captured, or commit a
mocks.json the whole team shares.
Storage
Read and edit SharedPreferences, read Jetpack DataStore, browse SQLite and Room with an optional SQL console. Encrypted preferences are decrypted for viewing but never edited.
Storage panel → WWorkManager
Every persisted work spec — yours and any library's — with state, constraints, attempts, backoff, input and output data, and the last stop reason.
WorkManager panel → CCrashes and ANRs
Uncaught exceptions are captured before the process dies. ANRs and native crashes are reported on the next launch on Android 11+, including the OS thread dump.
Crashes and ANRs → RRuntime
Build type, version, target SDK, device model, memory, locale, dark mode, session id, and your own metadata — the details a bug report always asks for.
Runtime panel → LLogcat
A live tail of your app's own log, filtered on the device by level, tag or regex. No cable, no permission, and nothing written to disk — the buffer lives in memory and dies with the process.
Logcat panel → VValue viewer
One full-screen JSON tree for any large value, from any panel: collapse and expand, child counts, escaped JSON opened as a real subtree, and search that stays responsive on huge payloads.
Value viewer →The person debugging, and the person signing off on the build.
Fewer round trips to reproduce a bug
The inspector is already in the build under test, so the evidence comes from the run that actually failed rather than from a second attempt on someone else's machine.
- Answer "what did the API return?" without a proxy, a certificate, or a developer sitting next to the tester.
- Force the edge case — a 500, an empty list, a timeout, an endpoint that does not exist yet — with a mock rule instead of a backend change.
- Attach a real artifact to the ticket: a HAR file, a cURL command, a stack trace, an ANR thread dump, the log lines around the failure.
- Check the state behind the screen — a stale preference, a row that never got written, a job stuck in
ENQUEUED.
One library instead of a debug screen per app
Most Android teams eventually build their own internal debug menu. This is that screen, maintained outside your codebase and consistent across every app you ship.
- Adopt it like any dependency from Maven Central — no vendor account, no server to run, nothing to procure. It works in your staging, UAT and internal-test variants too, on one line.
- Decide what testers can see and change: mask sensitive values, make storage read-only, hide panels, or block mocking outright.
- Keep it out of production with a compile-time no-op artifact, backed by a runtime gate that refuses to run in a non-debuggable build.
- Nothing leaves the device — no telemetry, no network calls of its own, no Logcat output of captured payloads.
A tool like this must never ship to users.
It does not, and you do not have to remember anything for that to stay true.
Release builds never get the inspector. They get a stub of the same name:
about 8 KB, four classes that do nothing, a manifest
with no components and no permissions, and not a single call into an
android.* API. Your code still compiles against it. There is
simply no inspection code in the APK for anyone to switch on.
That stub is verified, not just intended. Before a release can go out, the build opens the packaged artifact and looks inside it. If a component, a permission, a resource or any unexpected class has crept in, publishing to Maven Central stops there.
The second layer covers the case where the dependency is wired wrongly and
the real library ships anyway. It checks whether the build is debuggable,
and if it is not, it turns itself off — no database, no crash
handler, nothing captured, and every open() call refused.
Neither layer needs the other in order to work.
Two lines to try it.
One line adds the inspector to your debug builds, the other puts the empty stub in release. The version below is a placeholder — Maven Central has the current number.
That really is the whole setup. There is no initialisation call and no
Application subclass to write: the library starts itself, and
every panel, the shake gesture, crash capture and the log tail are on from
the first run.
Network capture is the one exception, because OkHttp only shows what its interceptors see. Add AppInspect's interceptor to your client and make it the last one in the chain — the install guide has the line and explains why the order matters.
Would you rather see it running before you commit to it? The sample app is a real Android project with the library already wired in. Clone it, run the debug build, and you are looking at the inspector on your own device a few minutes later.
dependencies {
debugImplementation("io.github.appinspect:appinspect:<latest-version>")
releaseImplementation("io.github.appinspect:appinspect-no-op:<latest-version>")
}
Questions people ask before adopting it.
Is AppInspect free?
Yes. There is no paid tier, no license fee, and no usage limit. It is published on Maven Central at no cost.
Does AppInspect add any code to my release APK?
Almost none, provided you use the appinspect-no-op artifact for release builds. That artifact is about 8 KB: four classes, a manifest with no components and no permissions, no resources, and no reference to any android.* API — the interceptor body is literally chain.proceed(chain.request()). Inspection code is absent from the release APK, and the library cannot be published to Maven Central unless a build check confirms that is still true. The security model has the full audit.
What can AppInspect inspect?
OkHttp requests and responses, SharedPreferences (including decrypted EncryptedSharedPreferences), Jetpack DataStore, SQLite and Room databases, WorkManager job queues, uncaught exceptions, ANRs and native crashes on Android 11+, a live logcat tail of your own process, and app, device and session metadata.
Will it run in our staging, UAT or internal-test build?
Yes, and usually with no extra work. AppInspect never sees your variant names — it looks at whether the build is debuggable. If your variant sets isDebuggable = true, it already works there. If it stays non-debuggable, one line in the build type opts it in: resValue("bool", "appinspect_enabled_in_non_debuggable_build", "true"). That value cannot leak into your release variant, because Android never merges one variant's resources into another. See builds and environments.
Do we need Compose or Kotlin in our app?
Neither. The inspector's Compose UI is already compiled into the published artifact, so a View and XML app gets it unchanged without enabling the Compose compiler. Java-only hosts work too. You also do not need a DI framework, an Application subclass, or any manifest change. See compatibility.
Can a tester read logcat without a cable?
Yes — that is the Logcat panel. It tails your app's own log output on the device, with level, tag, text and regex filters, and shares the filtered lines as a file. No permission is needed because capture is limited to your own process, which is the only log Android lets an app read, and nothing is written to disk: the buffer is in memory and dies with the process.
Can I mock API responses without changing my app's code?
Yes. A rule replaces a live OkHttp response with one you define — status code, headers, body, an artificial delay, or a simulated timeout or IO failure. Rules can be created in the panel, prefilled from a call you just captured, or committed to your repository as mocks.json. Because a mock is a real OkHttp response, your app's own logic runs unchanged. See Mocks.
Does AppInspect require root access?
No. It works on any unrooted device running Android 7.0 (API 24) or newer, with no special permissions beyond what a normal debug build already has.
Is my app's data safe in release builds?
Two independent layers protect it. At compile time, the no-op artifact contains no inspection code. At runtime, even the full module detects a non-debuggable build and disables itself completely — no database, no crash handler, no callbacks, and all open() calls refused.
Does AppInspect collect or send any data outside the device?
No. The library makes no network calls of its own and has zero telemetry. Captured network logs, storage values and crash traces stay on the device. See data handling.
How is this different from Charles Proxy or Flipper?
It runs on-device, inside the app. There is no desktop proxy, no SSL certificate to install, no Wi-Fi interception to configure, and no USB cable. A tester can open the inspector on their own phone without a developer or a second tool involved.
What Android versions are supported?
Android 7.0 (API 24) and above. ANR and native crash detection need Android 11 (API 30), because they rely on the OS ApplicationExitInfo API.
Can testers use it without developer help?
Yes. Once a developer has added the library, testers open the inspector themselves through a shake, a launcher shortcut, or a long press. No Android Studio, adb, or cable is needed during testing. See Opening the inspector.
How do I integrate it into my project?
Add debugImplementation for the full library and releaseImplementation for the no-op stub. AppInspect initialises itself through AndroidX Startup, so no Application class change is needed for basic use. For network capture, add .addAppInspectInterceptor() to your OkHttpClient builder, last. The install guide has the full steps.
Is there a sample app I can run?
Yes. AppInspect-Sample-App is a small Kotlin project with the library already wired in. Clone it, run the debug variant on a device or emulator, tap a few buttons to generate API calls, storage writes, a background job and a crash, then open the inspector and see what it captured. It is also the shortest integration reference there is — three files, all commented. The install guide has the clone command.
Can I export what AppInspect captured?
Yes. Network logs export as .txt with cURL commands, or as .har for browser DevTools and Proxyman. Mock rules export as .json, and the filtered log tail as .txt. Crash reports and runtime summaries go through the Android share sheet. Exports can contain real credentials — data handling explains what is in them and the QA checklist explains how to handle them.
Why don't network notifications appear on Android 13+?
Because the POST_NOTIFICATIONS runtime permission has not been granted. AppInspect declares it but has no Activity of its own, so it cannot prompt — your app has to request it. Without the grant, notifications silently never appear. See Network notifications.
Add it to your next debug build.
Two Gradle lines, one interceptor, and the inspector is on the device the next time anyone runs the app.
AppInspect is free. If it saved you an afternoon, that is enough of a reason.
There is no subscription and nothing gated. Contributions go towards keeping the library maintained, documented and released.