> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Performance

> Performance optimization strategies for the Android SDK

# Performance

This guide covers performance optimization strategies for the Privacy Boost Android SDK. The native binary is small enough that bundle size is rarely the bottleneck — the costs to manage are SDK init, proof generation, balance refresh frequency, and coroutine hygiene.

## SDK Lifecycle

### One Instance Per Process

`PrivacyBoost` holds a JWT, identity keys, and an internal note cache. Initialize it once and inject it via your DI container of choice (Hilt, Koin, manual `Application`-scoped singleton).

```kotlin theme={null}
class App : Application() {
    lateinit var privacyBoost: PrivacyBoost
        private set

    override fun onCreate() {
        super.onCreate()
        val config = PrivacyBoostConfig(
            serverUrl = "https://optimism.sepolia.privacyboost.io",
            chainId = null,
            shieldContractAddress = null,
            wethContractAddress = "0x4200000000000000000000000000000000000006",
            teePublicKey = null,
            appId = "app_abc123xyz",
            persistenceStorage = null,
            persistenceUnlock = null,
        )
        privacyBoost = PrivacyBoost(config)
    }
}
```

Re-instantiating the SDK on every screen would re-derive caches, refetch the merkle tree, and force a fresh JWT.

### Defer Init Until First Use

If your app has flows that don't touch the privacy pool (onboarding, marketing screens), defer SDK construction until the first gated route. The Rust core does some setup work on first call — keep it off the cold-start path.

```kotlin theme={null}
class PrivacyBoostHolder(private val config: PrivacyBoostConfig) {
    @Volatile private var instance: PrivacyBoost? = null

    fun get(): PrivacyBoost =
        instance ?: synchronized(this) {
            instance ?: PrivacyBoost(config).also { instance = it }
        }
}
```

## Coroutine Scope Hygiene

All network/proof methods are `suspend` — call them from a structured scope, not a fire-and-forget `GlobalScope.launch`.

### Use Lifecycle-Aware Scopes

```kotlin theme={null}
class WalletViewModel(
    private val sdk: PrivacyBoost,
) : ViewModel() {
    fun shield(token: String, amount: String) = viewModelScope.launch {
        runCatching { sdk.shield(token, amount) }
            .onSuccess { /* update UI */ }
            .onFailure { /* show error */ }
    }
}
```

`viewModelScope` cancels when the ViewModel is cleared, so a deposit that's still proving when the user navigates away gets cleanly cancelled instead of leaking.

### Run on the Right Dispatcher

The SDK does its own native work and is safe to call from `Dispatchers.Main`, but if you're chaining heavy serialization or DB writes around it, hop to `Dispatchers.Default` or `Dispatchers.IO`:

```kotlin theme={null}
val result = withContext(Dispatchers.IO) {
    sdk.shield(token, amount)
}
```

## Balance Refresh

### Don't Poll From Every Composable

`getBalance(token)` and `getAllBalances()` hit the network. Centralize refreshes through a single repository / `StateFlow` and let UI subscribe.

```kotlin theme={null}
class BalanceRepository(
    private val sdk: PrivacyBoost,
    private val staleness: Duration = 30.seconds,
) {
    private val _balances = MutableStateFlow<Map<String, TokenBalance>>(emptyMap())
    val balances: StateFlow<Map<String, TokenBalance>> = _balances.asStateFlow()

    private var lastFetch: Instant = Instant.DISTANT_PAST
    private val mutex = Mutex()

    suspend fun refresh(force: Boolean = false) = mutex.withLock {
        val now = Clock.System.now()
        if (!force && now - lastFetch < staleness) return@withLock
        val fresh = sdk.getAllBalances().associateBy { it.tokenAddress }
        _balances.value = fresh
        lastFetch = now
    }
}
```

The `Mutex` collapses concurrent calls — overlapping refreshes wait on the in-flight one.

### Refresh on Resume, Not on a Timer

Tying refresh to a `delay()` loop drains battery. Tie it to lifecycle events:

```kotlin theme={null}
@Composable
fun WalletScreen(repo: BalanceRepository) {
    val lifecycleOwner = LocalLifecycleOwner.current
    val balances by repo.balances.collectAsStateWithLifecycle()

    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME) {
                lifecycleOwner.lifecycleScope.launch { repo.refresh() }
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
    }

    BalanceList(balances.values.toList())
}
```

For periodic background refresh outside the app, use `WorkManager` with a minimum-interval periodic request — not a foreground service.

## Network

### Parallelize Independent Reads

Sequential awaits serialize unnecessarily. Use `async`:

```kotlin theme={null}
coroutineScope {
    val balances = async { sdk.getAllBalances() }
    val history  = async { sdk.getTransactionHistory(null, 20u, null) }
    val fees     = async { sdk.getFees(tokenId) }

    Triple(balances.await(), history.await(), fees.await())
}
```

### Page History — Don't Fetch It All

Pass a small `limit` and use the returned cursor for pagination:

```kotlin theme={null}
val firstPage = sdk.getTransactionHistory(
    txType = null,
    limit = 20u,
    cursor = null,
)
// later
firstPage.next?.let { cursor ->
    val nextPage = sdk.getTransactionHistory(
        txType = null,
        limit = 20u,
        cursor = cursor,
    )
}
```

## Memory

### Trim When Backgrounded

If your app stays in the background long enough to receive `onTrimMemory(TRIM_MEMORY_BACKGROUND)` or higher, drop non-essential caches — pending transaction lists, off-screen history pages — but keep the SDK instance.

```kotlin theme={null}
class App : Application(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            balanceRepo.evictNonCurrentTokens()
            transactionRepo.evictBeyondVisibleWindow()
        }
    }
    override fun onConfigurationChanged(newConfig: Configuration) {}
    override fun onLowMemory() {}
}
```

### Use Stable Keys in Lists

In LazyColumn, key transactions by `txHash` so Compose reuses items instead of rebuilding:

```kotlin theme={null}
LazyColumn {
    items(transactions, key = { it.txHash }) { tx ->
        TransactionRow(tx)
    }
}
```

## Operation Timing

Wrap SDK calls to observe timing in development:

```kotlin theme={null}
suspend inline fun <T> timed(label: String, block: () -> T): T {
    val start = System.nanoTime()
    return try {
        block()
    } finally {
        val ms = (System.nanoTime() - start) / 1_000_000
        Log.d("PB", "$label: ${ms}ms")
    }
}

val result = timed("shield") { sdk.shield(token, amount) }
```

In production, route timings into your APM (Firebase Performance, Sentry Performance, etc.) to surface slow operations as percentile histograms.

## What the SDK Already Optimizes

Things you do **not** need to layer on top:

* **Note cache** — the SDK caches unspent notes locally and only refetches new merkle tree leaves.
* **Merkle tree pruning** — proven nodes are kept; the rest are pruned.
* **JWT reuse** — `clearSession()` keeps identity keys so the next `authenticate()` is fast.

## Best Practices Summary

1. One SDK instance per process — hold it in `Application` or your DI graph.
2. Defer SDK init until the first privacy-pool screen.
3. Use `viewModelScope` / `lifecycleScope` — never `GlobalScope`.
4. Refresh balances on `ON_RESUME` and after writes, not on a timer.
5. Deduplicate concurrent refreshes with a `Mutex` or single shared flow.
6. Parallelize independent reads with `async`.
7. Page transaction history; don't fetch it all.
8. Use `clearSession()` over `logout()` when re-auth is expected soon.

## Next Steps

* [Error Handling Patterns](./error-handling)
* [Wallet Integration](../guides/wallet-integration)
