Android SDK (Native Kotlin)
com.appspacer:sdk — Asset OTA updates and crash reporting for native Android apps.
Update model: Google Play prohibits executable code hot-swap for native apps. AppSpacer for Android delivers asset OTA updates — JSON configs, images, data files — that your app reads from the OTA cache at runtime.
Requirements
| Minimum | |
|---|---|
| Android minSdk | 21 |
| compileSdk | 34 |
| Kotlin | 1.9+ |
Installation
Add the library to your module’s build.gradle:
dependencies {
implementation 'com.appspacer:sdk:1.0.0'
}Add permissions to AndroidManifest.xml (included automatically if using the AAR):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />Initialization
Initialize in your Application.onCreate():
import com.appspacer.sdk.AppSpacer
import com.appspacer.sdk.AppSpacerConfig
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
AppSpacer.initialize(
context = this,
config = AppSpacerConfig(
apiKey = "your_api_key",
appVersion = "1.0.0",
)
)
}
}Config Options
| Property | Type | Default | Description |
|---|---|---|---|
apiKey | String | Required | Your AppSpacer API key |
appVersion | String | Required | Current app version |
updateStrategy | UpdateStrategy | ON_INIT | ON_INIT, BACKGROUND, or MANUAL |
debugLogging | Boolean | false | Verbose logcat logging |
crashReportingEnabled | Boolean | true | Capture uncaught exceptions |
Reading OTA Assets
Always provide a fallback to your bundled resource:
import com.appspacer.sdk.AppSpacer
// In a coroutine:
val flags = AppSpacer.readAssetJson("config/feature-flags.json")
val showNewUi = flags?.get("new_ui") as? Boolean ?: false
// Raw bytes
val imageBytes = AppSpacer.readAsset("images/banner.png")
// UTF-8 string
val csvContent = AppSpacer.readAssetString("data/products.csv")Manual Update Check
lifecycleScope.launch {
val result = AppSpacer.checkForUpdates()
when (result.status) {
OTAUpdateStatus.UPDATED -> Log.d("OTA", "Updated ${result.updatedCount} asset(s)")
OTAUpdateStatus.UP_TO_DATE -> Log.d("OTA", "Already up to date")
OTAUpdateStatus.NO_NETWORK -> Log.w("OTA", "Offline")
OTAUpdateStatus.FAILED -> Log.e("OTA", result.error ?: "Unknown error")
}
}Crash Reporting
Uncaught exceptions are captured via Thread.setDefaultUncaughtExceptionHandler. Pending reports are flushed on next launch.
// Report a handled error
try {
riskyOperation()
} catch (e: Exception) {
AppSpacer.reportError(e, context = "PaymentFlow")
}
// Breadcrumbs
AppSpacer.addBreadcrumb("User tapped checkout", category = "navigation")
AppSpacer.addBreadcrumb("Cart loaded", category = "data", level = BreadcrumbLevel.INFO)API Reference
| Method | Description |
|---|---|
AppSpacer.initialize(context, config) | Initialize — call in Application.onCreate() |
AppSpacer.checkForUpdates() | suspend — check and download new assets |
AppSpacer.readAsset(key) | Read cached asset as ByteArray |
AppSpacer.readAssetString(key) | Read cached asset as String |
AppSpacer.readAssetJson(key) | Read and parse cached JSON asset |
AppSpacer.clearAssetCache() | Delete all cached assets |
AppSpacer.cacheSize() | Total cached bytes |
AppSpacer.reportError(throwable) | Report a handled error |
AppSpacer.addBreadcrumb(message) | Add crash context breadcrumb |
AppSpacer.sessionId | Current session identifier |
Backend Integration
GET /api/sdk/android/manifest
Headers: x-api-key: <your_api_key>
Query: app_version, current_bundle_id, install_id
POST /api/sdk/android/report-status
Body: { release_id, device_id, app_version, status, error_message }Send
install_id(a stable per-device ID) on every manifest call — it is required for staged-rollout targeting and monthly active-user metering. Reportinstalled/failedback toreport-statusso egress is billed once per device and failed installs can trigger auto-rollback.
Upload asset bundles from the dashboard with --platform android.