Android Interceptor
OkHttp interceptor that routes traffic through Mock Alpha in debug builds
Android Interceptor
A reference implementation of the capture-and-mock interceptor pattern (see Client Integration) for Android, built on OkHttp. When enabled, it sends every request through Mock Alpha and simultaneously reports the real response back so Mock Alpha can capture and seed scenarios.
Android is just one platform — the same pattern works anywhere. See Platform Examples for Flutter, Web, iOS, and curl versions.
Setup
1. Add the interceptor class
Create MockAlphaInterceptor.kt in your Android project:
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.json.JSONObject
class MockAlphaInterceptor(
private val mockAlphaUrl: String = BuildConfig.MOCK_ALPHA_URL,
private val enabled: Boolean = BuildConfig.DEBUG,
private val apiKey: String = BuildConfig.MOCK_API_KEY,
private val serviceName: String = "android-app",
) : Interceptor {
private val reportingClient = OkHttpClient()
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
if (!enabled) return chain.proceed(original)
// 1. Get the real response
val realResponse = chain.proceed(original)
val bodyString = realResponse.body?.string() ?: ""
// 2. Report to Mock Alpha (fire-and-forget)
reportCapture(original, realResponse.code, bodyString)
// 3. Re-route the request through Mock Alpha mock endpoint
val mockRequest = original.newBuilder()
.url("$mockAlphaUrl/api/mock${original.url.encodedPath}".toHttpUrl()
.newBuilder()
.encodedQuery(original.url.encodedQuery)
.build())
.header("X-Api-Key", apiKey)
.build()
return try {
chain.proceed(mockRequest)
} catch (e: Exception) {
// Fallback to real response if mock is unavailable
realResponse.newBuilder()
.body(bodyString.toByteArray().let {
okhttp3.ResponseBody.create("application/json".toMediaType(), it)
})
.build()
}
}
private fun reportCapture(request: Request, statusCode: Int, body: String) {
try {
val payload = JSONObject().apply {
put("method", request.method)
put("path", request.url.encodedPath)
put("statusCode", statusCode)
put("responseBody", runCatching { JSONObject(body) }.getOrNull())
put("serviceName", serviceName)
}
val reportRequest = Request.Builder()
.url("$mockAlphaUrl/api/collect")
.header("X-Api-Key", apiKey)
.post(payload.toString().toRequestBody("application/json".toMediaType()))
.build()
reportingClient.newCall(reportRequest).execute().close()
} catch (_: Exception) {
// Silent — reporting failures must not crash the app
}
}
}2. Register the interceptor
In your OkHttpClient builder (typically in a Hilt or Koin module):
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(MockAlphaInterceptor(apiKey = BuildConfig.MOCK_API_KEY))
.build()3. Add the URL and API key to build config
In app/build.gradle.kts:
android {
defaultConfig {
buildConfigField(
"String", "MOCK_ALPHA_URL",
"\"${project.findProperty("MOCK_ALPHA_URL") ?: "https://mock-alpha.your-domain.com"}\""
)
buildConfigField(
"String", "MOCK_API_KEY",
"\"${project.findProperty("MOCK_API_KEY") ?: ""}\""
)
}
}Add to your local.properties:
MOCK_ALPHA_URL=https://mock-alpha.your-domain.com
MOCK_API_KEY=<your-key>The key is generated when you create a project in Mock Alpha and can be found in Project Settings. With a deployed (HTTPS) instance, this is all the configuration you need.
4. Local instance only — allow cleartext HTTP
Skip this step if you use a deployed HTTPS instance. If Mock Alpha runs on your machine (http://10.0.2.2:3000 from the emulator), Android blocks cleartext HTTP by default. Add res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">10.0.2.2</domain>
</domain-config>
</network-security-config>Reference it from AndroidManifest.xml:
<application
android:networkSecurityConfig="@xml/network_security_config"
...>How it works
Android Request
│
├── [real] → Backend API → real response → /api/collect → Mock Alpha captures
│
└── [mock] → Mock Alpha /api/mock/... → scenario response → AndroidOn each request the interceptor:
- Forwards the request to the real backend and captures the response
- Reports it to
/api/collectso Mock Alpha learns the endpoint shape - Returns the mock scenario response from Mock Alpha
Once an endpoint is captured, you can switch scenarios from the dashboard. The Android app always gets Mock Alpha's response — your selected scenario.
Choosing MOCK_ALPHA_URL
| Environment | Mock Alpha URL |
|---|---|
| Deployed instance (recommended) | https://mock-alpha.your-domain.com |
| Local + Android Emulator | http://10.0.2.2:3000 |
| Local + physical device (same Wi-Fi) | http://192.168.x.x:3000 |
| Local + physical device (USB tunnel) | http://localhost:3000 (after adb reverse tcp:3000 tcp:3000) |
MQTT connection
If you are using the MQTT mock server, connect your MQTT client with:
val mqttOptions = MqttConnectOptions().apply {
userName = projectId // the UUID shown in Project Settings
password = apiKey.toCharArray()
}
val client = MqttClient("tcp://mock-alpha.your-domain.com:1883", clientId)
client.connect(mqttOptions)
// Subscribe without the projectId prefix — Mock Alpha adds it internally
client.subscribe("devices/status") { topic, message ->
// handle mock MQTT events
}(For a local instance from the emulator, use tcp://10.0.2.2:1883.)
See MQTT Mock Server for how to configure topics and payloads.