---
title: "Android Integration Guide"
canonical_url: "https://apidocs.sportradar.com/resources/sport-sdk/docs/integration/android"
markdown_url: "https://apidocs.sportradar.com/resources/sport-sdk/docs/integration/android.md"
last_updated: "2026-06-10T08:48:04Z"
---

# Android Integration Guide

This guide shows the fastest path to a working Android integration:

1. add the SDK dependency
2. initialize `SportSdk`
3. fetch sports
4. navigate into seasons, fixtures, and match details

Reference implementation:

- `androidApp/src/main/java/ag/sportradar/mobile/sdk/sport/android/ui/SampleActivity.kt`
- `androidApp/src/main/java/ag/sportradar/mobile/sdk/sport/android/viewmodel/`

> **Quick start**
>
> Add the dependency, call `SportSdk.init(...)` once, wait for it to finish, and verify the setup with `SportControllers.sportsController.getAllSports()`.

```kotlin
dependencies {
    implementation("ag.sportradar.mobile.sdk.sport:sport-sdk:<version>")
}

val initialized = SportSdk.init(
    applicationContext,
    enableAnalytics = true,
    clientConfig = ClientConfig(
        clientId = YOUR_CLIENT_ID,
        appKey = "YOUR_APP_KEY",
        logger = if (BuildConfig.DEBUG) DebugAntilog() else null,
    ),
)

if (initialized) {
    val sports = SportControllers.sportsController.getAllSports().data.orEmpty()
    println("Loaded ${sports.size} sports")
}
```

## Start here

### Install

Use Maven Central in consumer apps, or depend on `:sport-sdk` locally while developing in this repository.

### Initialize

Call `SportSdk.init(...)` before any controller usage and gate your UI until it completes.

### Validate

Use `getAllSports()` as your first request to confirm credentials and connectivity.

### Go deeper

From sports, move to categories, seasons, fixtures, standings, lineups, timeline events, and squads.

## Prerequisites

> **Android requirements**
>
> - Android 8.0 / API 26 or later
> - Kotlin + coroutines
> - Sportradar credentials: `clientId`, `appKey`
>
> `clientId` is an `Int` on Android.
>
> If `appIdentifier` is still passed through a shared configuration path, the SDK ignores it on Android and always uses the app `packageName`.

## Installation

**Maven Central**

If your team consumes the published Android artifact from Maven Central, no custom repository URL is required.

Make sure your project resolves dependencies from `google()` and `mavenCentral()`.

```kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}
```

Then add the dependency in your app module:

```kotlin
dependencies {
    implementation("ag.sportradar.mobile.sdk.sport:sport-sdk:<version>")
}
```

If your project already includes `mavenCentral()`, you only need the dependency line.

You can also keep the version in a version catalog if that matches your Gradle setup:

```toml
[versions]
sportSdk = "<version>"

[libraries]
sport-sdk = { module = "ag.sportradar.mobile.sdk.sport:sport-sdk", version.ref = "sportSdk" }
```

```kotlin
dependencies {
    implementation(libs.sport.sdk)
}
```

**Local module**

If you are working directly in this repository or embedding the source module locally, include the module and depend on it as a project:

```kotlin
dependencies {
    implementation(project(":sport-sdk"))
}
```

> **Note**
>
> Replace `<version>` with the released SDK version your team is onboarding to. Until the Maven Central listing URL is finalized, the artifact coordinates above are the important part.

## Android permissions

Add the required network permissions to your `AndroidManifest.xml`:

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
```

## Quick start

Initialize the SDK before you use any controller.

```kotlin
class SampleActivity : AppCompatActivity() {
    private var sdkInitialized by mutableStateOf(false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val splashScreen = installSplashScreen()
        splashScreen.setKeepOnScreenCondition { !sdkInitialized }

        lifecycleScope.launch {
            val initialized = SportSdk.init(
                applicationContext,
                enableAnalytics = true,
                clientConfig = ClientConfig(
                    clientId = YOUR_CLIENT_ID,
                    appKey = "YOUR_APP_KEY",
                    logger = if (BuildConfig.DEBUG) DebugAntilog() else null,
                ),
            )

            sdkInitialized = initialized
        }

        setContent {
            if (sdkInitialized) {
                MainScreen()
            }
        }
    }
}
```

1. **Step 1**

   #### Initialize the SDK

   Create `ClientConfig`, provide your credentials, and wait for `SportSdk.init(...)` to finish.

2. **Step 2**

   #### Gate the UI until initialization completes

   Keep splash / loading UI visible until `sdkInitialized` becomes `true`, then render your main screen.

> **Tip**
>
> Logging is configured through `ClientConfig.logger`, not a separate `enableLogs` parameter on Android.

> **Warning**
>
> Do not call `SportControllers.*` until initialization completes.

> **Note**
>
> `throwErrorOnInit` defaults to `false` and should only be enabled for testing failure handling.

## First successful request

Start with `getAllSports()`.

```kotlin
suspend fun loadSports() {
    val result = SportControllers.sportsController.getAllSports()

    if (result.isSuccess) {
        val sports = result.data.orEmpty()
        println("Loaded ${sports.size} sports")
    } else {
        println("Failed to load sports: ${result.errorMessage}")
    }
}
```

## Result handling model

### What you get back

Android APIs return `CommonResult<T>` or typed aliases built on top of it.

Some typed aliases also expose convenience accessors backed by `data`, for example `result.sports`, `result.matches`, `result.events`, or `result.teamSquad`.

### What to check

- `result.isSuccess`
- `result.data`
- `result.errorMessage`

### Common aliases

- `SportsResult = CommonResult<List<Sport>>`
- `SeasonsResult = CommonResult<List<Season>>`
- `StandingsResult = CommonResult<Standings>`
- `FixturesResult = CommonResult<List<Match>>`
- `MatchStatisticsResult = CommonResult<List<MatchStatistics>>`

## Public controllers

### `SportControllers.sportsController`

Sports, categories, dated matches, and tracked category updates.

### `SportControllers.seasonController`

Tournament seasons, tables / standings, and season fixtures.

### `SportControllers.matchController`

Statistics, lineups, and soccer timeline events.

### `SportControllers.playerController`

Team squad data for a specific team + season pair.

## Sports feed

### Available methods

| Method                              | Returns                           |
| ----------------------------------- | --------------------------------- |
| `getAllSports()`                    | `SportsResult`                    |
| `getSportCategories(sportId)`       | `SportsResult`                    |
| `getSportMatchesForDate(timestamp)` | `SportsResult`                    |
| `trackSportCategories(sportId)`     | `Flow<CommonResult<List<Sport>>>` |

### Example ViewModel pattern

```kotlin
class SportsViewModel : ViewModel(), InternalKoinComponent {
    private val ioDispatcher: CoroutineDispatcher by inject(qualifier(DispatcherType.IO))
    private val sportsController = SportControllers.sportsController
    private val _sportsState = MutableStateFlow(SportsState())

    val sportsState: StateFlow<SportsState> = _sportsState

    fun getAllSports() {
        _sportsState.update { it.copy(loadingStatus = LoadingStatus.Loading, sports = emptyList()) }

        viewModelScope.launch(ioDispatcher) {
            val result = sportsController.getAllSports()
            _sportsState.update {
                if (result.isSuccess) {
                    it.copy(loadingStatus = LoadingStatus.Idle, sports = result.data.orEmpty())
                } else {
                    it.copy(loadingStatus = LoadingStatus.Error(result.errorMessage ?: "Failed to load sports"))
                }
            }
        }
    }
}
```

> **Tip**
>
> `LoadingStatus` is provided by the SDK in `ag.sportradar.mobile.sdk.sport.state`. Reuse it rather than redefining your own loading enum.

## Seasons, tables, fixtures, and squad

**SeasonController**

| Method                                         | Returns           |
| ---------------------------------------------- | ----------------- |
| `getTournamentSeasons(uniqueTournamentId)`     | `SeasonsResult`   |
| `getSeasonStanding(seasonId, isCurrentSeason)` | `StandingsResult` |
| `getSeasonFixtures(seasonId, isCurrentSeason)` | `FixturesResult`  |

```kotlin
val seasonsResult = SportControllers.seasonController.getTournamentSeasons(uniqueTournamentId)
val fixturesResult = SportControllers.seasonController.getSeasonFixtures(seasonId, isCurrentSeason = true)

if (seasonsResult.isSuccess) {
    val seasons = seasonsResult.data.orEmpty()
}

if (fixturesResult.isSuccess) {
    val matches = fixturesResult.data.orEmpty()
}
```

**PlayerController**

| Method                           | Returns           |
| -------------------------------- | ----------------- |
| `getTeamSquad(teamId, seasonId)` | `TeamSquadResult` |

```kotlin
val squadResult = SportControllers.playerController.getTeamSquad(teamId, seasonId)

if (squadResult.isSuccess) {
    val squad = squadResult.data
}
```

### ID types to remember

- `sportId`: `Long`
- `uniqueTournamentId`: `Long`
- `seasonId`: `Long`
- `matchId`: `Long`
- `teamId`: `Int`

## Match details

### MatchController methods

| Method                                  | Returns                 |
| --------------------------------------- | ----------------------- |
| `getMatchStatistics(matchId)`           | `MatchStatisticsResult` |
| `getMatchLineups(matchId)`              | `MatchLineupsResult`    |
| `getSoccerMatchTimelineEvents(matchId)` | `SoccerTimelineResult`  |

### Recommended pattern: load in parallel

```kotlin
viewModelScope.launch(ioDispatcher) {
    coroutineScope {
        val statisticsDeferred = async { SportControllers.matchController.getMatchStatistics(matchId) }
        val lineupsDeferred = async { SportControllers.matchController.getMatchLineups(matchId) }
        val timelineDeferred = async { SportControllers.matchController.getSoccerMatchTimelineEvents(matchId) }

        val statistics = statisticsDeferred.await()
        val lineups = lineupsDeferred.await()
        val timeline = timelineDeferred.await()

        // Update UI state from statistics.data / lineups.data / timeline.data
    }
}
```

### Working with sealed models

```kotlin
when (val stat = matchStatistic) {
    is MatchStatistics.IntStat -> Unit
    is MatchStatistics.DoubleStat -> Unit
    is MatchStatistics.TextStat -> Unit
    is MatchStatistics.BoolStat -> Unit
}

when (val event = soccerEvent) {
    is SoccerEvent.HighlightEvent -> Unit
    is SoccerEvent.StatusUpdate -> Unit
}
```

## Jetpack Compose example

**### Show a minimal Compose screen**

```kotlin
@Composable
fun SportsScreen(viewModel: SportsViewModel = viewModel()) {
    val state by viewModel.sportsState.collectAsStateWithLifecycle()

    LaunchedEffect(Unit) {
        viewModel.getAllSports()
    }

    when (val loadingStatus = state.loadingStatus) {
        is LoadingStatus.Initial, LoadingStatus.Loading -> CircularProgressIndicator()
        is LoadingStatus.Idle -> LazyColumn {
            items(state.sports) { sport ->
                Text(text = sport.name)
            }
        }
        is LoadingStatus.Error -> Text(loadingStatus.message)
    }
}
```

## Lifecycle and shutdown

> **Lifecycle checklist**
>
> - initialize once before first use
> - use `viewModelScope` / `lifecycleScope` for SDK calls
> - cancel tracking flows when the screen leaves
> - if your app explicitly tears down the SDK lifecycle, call `SportSdk.shutdown()` from a coroutine

## Proguard / R8

**### Add keep rules when your app shrinks aggressively**

```proguard
-keep class ag.sportradar.mobile.sdk.sport.** { *; }
-keep class ag.sportradar.mobile.sdk.sport.model.** { *; }
```

## Troubleshooting

**### Initialization returns `false`**

Check:

1. `clientId` is an `Int`
2. `appKey` is correct
3. your app has a valid application ID (`packageName`), which the SDK resolves automatically
4. your project can resolve the SDK artifact from Maven Central

**### No data returned**

Check:

1. `result.errorMessage`
2. timestamp values are in **milliseconds**
3. IDs are the correct type (`Long` vs `Int`)

**### Controllers crash or fail immediately**

Usually this means a controller was accessed before `SportSdk.init(...)` completed.

**### Need logs in development**

Attach a logger in `ClientConfig`:

```kotlin
ClientConfig(
    clientId = YOUR_CLIENT_ID,
    appKey = "YOUR_APP_KEY",
    logger = if (BuildConfig.DEBUG) DebugAntilog() else null,
)
```
