AppInspect
Android in-app inspector

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.

Maven Central · io.github.appinspect Android 7.0+ (API 24) Free, no account, no telemetry

Network AppInspect · debug
API log 198 calls captured · 52 with issues
500 GET 6711 ms
/api/v1/orders api-qa.example.co
11:55:13 77 B
503 GET MOCK 0 ms
/api/v1/payments rule: payments-down
11:55:09 SHORT_CIRCUIT
200 POST 1488 ms
/api/v1/auth/refresh api-qa.example.co
11:55:07 1.1 KB
200 GET 124 ms
/api/v1/user/profile api-qa.example.co
11:55:02 842 B
422 PUT 142 ms
/api/v1/cart/items/8812 api-qa.example.co
11:54:58 311 B
304 GET 18 ms
/api/v1/config/flags api-qa.example.co
11:54:55 cached
Runtime rules Made on this device — no rebuild
SHORT_CIRCUIT 503
GET */api/v1/payments Payments service down
OVERRIDE 200 2 s delay
GET /api/v1/orders Orders — empty state
Bundled rules From mocks.json, committed in git
SHORT_CIRCUIT OVERRIDDEN
GET /api/v1/config/flags Feature flags — all on
session_prefs.xml 14 keys · tap a row to edit
onboarding_done
true
user_tier
"gold"
last_sync_at
1755712493
cached_profile
{…} 214 KB
retry_count
3
🔒 ENCRYPTED
auth_secure_prefs.xml Decrypted for viewing — read only
Work specs 9 persisted · 1 failed, 3 enqueued
FAILED attempt 4/5
UploadReceiptWorker backoff EXPONENTIAL · 30s
ENQUEUED PERIODIC
SyncCatalogWorker waiting on: UNMETERED network
RUNNING
AnalyticsFlushWorker tag: analytics · expedited
SUCCEEDED 6 periods
CleanupCacheWorker every 6h, flex 1h
Crashes and ANRs 3 records · kept across restarts
ANR today 11:52
Input dispatching timed out main · OS thread dump attached
ApplicationExitInfo
CRASH today 09:14
NullPointerException OrderDetailFragment.kt:212
thread: main 42 frames
NATIVE yesterday
SIGSEGV in libimage.so reported on next launch
Live tail 1,284 of 2,000 in buffer · following

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 →

How it works

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.

01

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.

02

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.

03

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.

What you can inspect

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.

N

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 →
M

Mocks

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.

Response mocking →
S

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 →
W

WorkManager

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 →
C

Crashes 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 →
R

Runtime

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 →
L

Logcat

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 →
V

Value 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 →
Who it is for

The person debugging, and the person signing off on the build.

Developers and testers

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.
Companies and platform teams

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.
Release safety

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.

Quick start

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.

app/build.gradle.kts
dependencies {
    debugImplementation("io.github.appinspect:appinspect:<latest-version>")
    releaseImplementation("io.github.appinspect:appinspect-no-op:<latest-version>")
}
FAQ

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.

Maintainer

Built and maintained by Suryansh Prajapati.

AppInspect exists because most Android teams end up writing the same internal debug screen twice. Suggestions, bug reports and corrections to these docs are all welcome.

Support the work

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.