Use Maven Central
Snap-O publishes Android libraries version 3.1.1 and newer to
Maven Central.
Most Android projects already include
mavenCentral(); add it to your dependency sources if
yours does not.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
Add the Android dependency
Choose the dependency that matches your network client. Use the real interceptor in debug builds and its no-op counterpart in release builds. The no-op artifact preserves the same API while passing traffic through without starting the Snap-O server.
Add request interceptors
Add the interceptor once when the client is created. Requests made by that client can then be captured and made available to Snap-O. WebSockets require the wrapped factory shown below.
OkHttp
import com.openai.snapo.network.okhttp3.SnapOOkHttpInterceptor
val client = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()
Ktor with the OkHttp engine
Attach the same interceptor through Ktor's OkHttp engine.
import com.openai.snapo.network.okhttp3.SnapOOkHttpInterceptor
val client = HttpClient(OkHttp) {
engine {
addInterceptor(SnapOOkHttpInterceptor())
}
}
If your app already builds an OkHttp client, pass that client to Ktor instead.
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()
val client = HttpClient(OkHttp) {
engine {
preconfigured = okHttpClient
}
}
HttpURLConnection
Open the connection through the Snap-O interceptor. Response details are captured when your app reads the response code or response stream.
import com.openai.snapo.network.httpurlconnection.SnapOHttpUrlInterceptor
val interceptor = SnapOHttpUrlInterceptor()
val connection = interceptor.open(URL("https://example.com"))
connection.connect()
connection.inputStream.use { body ->
// Read the response.
}
connection.disconnect()
You can also wrap a connection created elsewhere.
val existing = URL("https://example.com")
.openConnection() as HttpURLConnection
val connection = SnapOHttpUrlInterceptor().intercept(existing)
WebSockets
Create WebSockets through the wrapped factory. Regular HTTP calls can continue using the original client.
import com.openai.snapo.network.okhttp3.withSnapOInterceptor
val webSocketFactory = client.withSnapOInterceptor()
val webSocket = webSocketFactory.newWebSocket(request, listener)
For Ktor, use the same preconfigured OkHttp client for HTTP
traffic, wrap its WebSocket factory, and install Ktor's
WebSockets plugin.
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()
val client = HttpClient(OkHttp) {
engine {
preconfigured = okHttpClient
webSocketFactory = okHttpClient.withSnapOInterceptor()
}
install(WebSockets)
}
Verify the connection
- Install and launch the debug build on an authorized Android device or emulator.
- Open Snap-O on macOS and select the connected device.
- Open Tools → Network Inspector, use the toolbar network icon, or press ⌘⌥I.
- Select the app process if prompted, then trigger a request in the Android app.
- Open the request to inspect headers, bodies, timing, SSE, or WebSocket messages.
If the Android app restarts, Snap-O may offer a newer process in the inspector sidebar. Switch to it to continue with live traffic.
Troubleshooting
- Confirm the device is online and authorized in
adb devices. - Confirm the installed variant includes the debug interceptor, not the no-op release artifact.
- Make sure the request uses the exact OkHttp client, Ktor engine, or HttpURLConnection wrapper you configured.
- Keep the Android app process running and select the newest process after an app restart.
- Update the Snap-O desktop app and Android dependency together if a protocol mismatch appears.
- The replay buffer retains up to five minutes, 10,000 events, or 16 MiB of total event data, whichever limit is reached first.
- Individual request and response bodies are captured up to 5 MiB by default; larger bodies are truncated.
Advanced setup
Debug builds initialize the on-device server automatically through
SnapONetworkInitProvider. Most apps should keep the
defaults.
Provider configuration
The provider supports automatic initialization, main-process filtering, a mode label, and replay limits. Manifest overrides must use Android manifest-merger directives because the library already declares these metadata entries.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<provider
android:name="com.openai.snapo.network.SnapONetworkInitProvider"
android:authorities="${applicationId}.snapo-network-init"
android:exported="false">
<meta-data
android:name="snapo.auto_init"
android:value="false"
tools:replace="android:value" />
</provider>
</application>
</manifest>
| Metadata key | Default | Purpose |
|---|---|---|
snapo.auto_init |
true |
Starts the inspector during app initialization. |
snapo.main_process_only |
true |
Restricts automatic initialization to the app's main process. |
snapo.mode_label |
safe |
Reports a custom mode label to Snap-O clients. |
snapo.allow_release |
false |
Allows the server in a non-debuggable build. Enable only intentionally. |
snapo.buffer_window_ms |
300000 |
Sets the rolling replay window in milliseconds. |
snapo.max_events |
10000 |
Caps the number of events retained for replay. |
snapo.max_bytes |
16777216 |
Caps total retained event data at 16 MiB. |
Manual initialization
If automatic initialization is disabled, initialize the
inspector from your application process with a custom
NetworkInspectorConfig.
import com.openai.snapo.network.NetworkInspector
import com.openai.snapo.network.NetworkInspectorConfig
import kotlin.time.Duration.Companion.minutes
NetworkInspector.initialize(
application,
NetworkInspectorConfig(
bufferWindow = 10.minutes,
maxBufferedEvents = 20_000,
maxBufferedBytes = 32L * 1024 * 1024,
modeLabel = "debug",
),
)