> For the complete documentation index, see [llms.txt](https://docs.hackle.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hackle.io/en/development-guide/android.md).

# Android

{% hint style="info" %}
Hackle Android SDK supports Android API 24 and above (7.0 Nougat).

* Starting from Hackle Android SDK version 4.0.0, the minimum supported version has been changed to Android API 24.
* Starting from Hackle Android SDK version 3.0.0, the minimum supported version has been changed to Android API 21.
* The minimum supported version for Hackle Android SDK 2.x is Android API 16 and above.
  {% endhint %}

## Add Dependency

[![](https://img.shields.io/maven-central/v/io.hackle/hackle-android-sdk)](https://central.sonatype.com/artifact/io.hackle/hackle-android-sdk)

```groovy
repositories {
  mavenCentral()
}
```

Add the dependency to your `build.gradle` file.

```groovy
dependencies {
  implementation 'io.hackle:hackle-android-sdk:2.66.1'
}
```

#### ProGuard

If you use ProGuard, obfuscation rules are automatically included in the aar artifact. Otherwise, you must include the rules below.

```
-keep class io.hackle.android.** { *; }
-keep class io.hackle.sdk.** { *; }
```

#### Third-party Dependencies

The Hackle Android SDK has the following third-party dependencies.

<table><thead><tr><th width="450.45703125">Library</th><th>Description</th></tr></thead><tbody><tr><td><code>com.squareup.okhttp3:okhttp:3.12.2</code></td><td>HTTP/HTTPS client</td></tr><tr><td><code>com.google.code.gson:gson:2.8.6</code></td><td>JSON serialization/deserialization</td></tr><tr><td><code>com.google.android.gms:play-services-base:17.3.0</code></td><td>Google Play Services common module</td></tr></tbody></table>

## SDK Initialization

You must initialize `HackleApp` before using the SDK.

* An SDK Key is required to initialize `HackleApp`.
* `HackleApp` is the class that provides methods for using SDK features.
* You can find the SDK Key in [SDK Integration Info](https://dashboard.hackle.io/config/sdk-setting) on the Hackle Dashboard.

Initialization runs **asynchronously**, fetching the necessary information from the Hackle server and storing it in the SDK.

{% hint style="warning" %}
**Do not call SDK features before initialization is complete.**

Initialization runs asynchronously and is only complete once synchronization with the Hackle server has finished. If you call SDK features before initialization is complete, the SDK may not work correctly or events may be lost.

* **A/B Tests, Feature Flags, and Remote Config do not work correctly.** Instead of the actual assignment result, they return the default group (`A`), off (`false`), or the default value passed in the call. (Reason: `SDK_NOT_READY`)
* **Exposure events are not recorded.** Even if a user was actually part of the experiment, they are not counted as exposed, which can distort the experiment results.
* **Events may be lost.** Events collected with `track()` may not be sent.
* **Events may carry an inaccurate session ID.**
* **In-App Messages are not displayed.** Trigger conditions are evaluated at the moment an event is collected, and events collected before initialization is complete are not re-evaluated afterwards.

Make sure to call SDK features only after initialization is complete.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import io.hackle.android.Hackle
import io.hackle.android.initialize

Hackle.initialize(applicationContext, YOUR_APP_SDK_KEY) {
  // SDK ready to use.
}
```

{% endtab %}

{% tab title="Java" %}

```java
import io.hackle.android.HackleApp;

HackleApp.initializeApp(getApplicationContext(), YOUR_APP_SDK_KEY);
```

{% endtab %}
{% endtabs %}

#### Recommended Initialization Strategy: Initialize via Splash Screen

Display a splash screen instead of immediately launching the app, and initialize the SDK. After initialization, close the splash screen via callback so the user can begin interacting with the app.

### Inject User on Initialization

You can initialize the SDK with user information included.

* If no user information is provided, the user information stored in local storage is used.
* If user information is provided, the user information stored in local storage is not used.
* If no user is injected and there is no user information in local storage, a user with the [Hackle Device ID](/en/getting-started/user-identifier.md) as the device ID is used.

{% hint style="info" %}
User information can be freely updated after SDK initialization using the user configuration functions.
{% endhint %}

{% hint style="warning" %}
User information injected at initialization is not merged with user information stored in local storage.

ex) If `userId: A` is stored and you inject `deviceId: B` at initialization,\
the user is set to `userId: null, deviceId: B`.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import io.hackle.android.Hackle
import io.hackle.android.initialize
import io.hackle.sdk.common.User

val user = User.builder()
    .userId("142")                  // User ID
    .deviceId("ae2182e0")           // Device ID
    .build()

Hackle.initialize(applicationContext, YOUR_APP_SDK_KEY, user) {
  // SDK ready to use.
}
```

{% endtab %}

{% tab title="Java" %}

```java
import io.hackle.android.HackleApp;
import io.hackle.sdk.common.User

User user = User.builder()
    .userId("142")                  // User ID
    .deviceId("ae2182e0")           // Device ID
    .build()

HackleApp.initializeApp(getApplicationContext(), YOUR_APP_SDK_KEY, user, () -> {
  // SDK ready to use.
});
```

{% endtab %}
{% endtabs %}

### SDK Initialization Config

You can initialize the SDK with a configuration object.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import io.hackle.android.Hackle
import io.hackle.android.HackleConfig
import io.hackle.android.initialize

val config = HackleConfig.builder()
  .build()

Hackle.initialize(applicationContext, YOUR_APP_SDK_KEY, config) {
  // SDK ready to use.
}
```

{% endtab %}

{% tab title="Java" %}

```java
import io.hackle.android.HackleApp;
import io.hackle.android.HackleConfig;

HackleConfig config = HackleConfig.builder()
  .build();

HackleApp.initializeApp(getApplicationContext(), YOUR_APP_SDK_KEY, config, () -> {
  // SDK ready to use.
});
```

{% endtab %}
{% endtabs %}

#### Configuration Options

<table data-full-width="false"><thead><tr><th width="222.29296875">Option</th><th width="295.48828125">Description</th><th width="125.8125">Default</th><th width="101.90234375">Min Version</th></tr></thead><tbody><tr><td><code>exposureEventDedupIntervalMillis</code><sup>*</sup></td><td><p>Removes duplicate exposure events for the same A/B Test or Feature Flag result triggered consecutively by the same user.</p><ul><li>Min: 1000 (1 second)</li><li>Max: 86400000 (24 hours)</li></ul></td><td>60000 (1 min)</td><td>2.7.0+</td></tr><tr><td><code>eventFlushIntervalMillis</code></td><td><p>The interval at which collected events are sent to the server.</p><ul><li>Min: 1000 (1 second)</li><li>Max: 60000 (1 minute)</li></ul></td><td>10000 (10 sec)</td><td>2.10.0+</td></tr><tr><td><code>pollingIntervalMillis</code></td><td><p>Periodically updates configuration set in the Dashboard.</p><ul><li>Min: 60000 (60 seconds)</li></ul></td><td>-1<br>(no periodic update)</td><td>2.19.0+</td></tr><tr><td><code>automaticScreenTracking</code></td><td>Whether to enable automatic Screen Tracking</td><td><code>true</code></td><td>2.39.0+</td></tr><tr><td><code>automaticAppLifecycleTracking</code></td><td>Whether to enable automatic app start/stop tracking</td><td><code>true</code></td><td>2.64.0+</td></tr><tr><td><code>sessionPolicy</code></td><td>Configures session persistence and expiration conditions.</td><td><p><code>ALWAYS_NEW_SESSION</code> ,</p><p><code>1800000</code></p></td><td>2.65.0+</td></tr><tr><td><code>optOutTracking</code></td><td>Whether to enable Opt-out.</td><td><code>false</code></td><td>2.65.0+</td></tr><tr><td><code>evaluationMode</code></td><td>Sets the evaluation mode.</td><td><code>LOCAL</code></td><td>4.0.0+</td></tr><tr><td><code>sessionTimeoutMillis</code><br>(deprecated)</td><td>Sets the session timeout duration. Use <code>sessionPolicy</code> instead.</td><td>1800000<br>(30 min)</td><td>2.13.0+</td></tr></tbody></table>

{% hint style="info" %} <sup>\*</sup> Supported even after app restart from version 2.47.0.\ <sup>\*</sup> For versions below 2.47.0, the maximum value is: 3600000 (1 hour).
{% endhint %}

#### Evaluation Mode Configuration

You can choose whether the SDK performs evaluation directly or fetches results that the Hackle server has already evaluated.\
If not configured, the SDK uses the default value `EvaluationMode.LOCAL` (local evaluation).

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import io.hackle.sdk.common.EvaluationMode

val config = HackleConfig.builder()
    .evaluationMode(EvaluationMode.REMOTE)
    .build()

Hackle.initialize(applicationContext, YOUR_APP_SDK_KEY, config) {
    // SDK ready to use.
}
```

{% endtab %}

{% tab title="Java" %}

```java
import io.hackle.sdk.common.EvaluationMode;

HackleConfig config = HackleConfig.builder()
    .evaluationMode(EvaluationMode.REMOTE)
    .build();

HackleApp.initializeApp(getApplicationContext(), YOUR_APP_SDK_KEY, config, () -> {
    // SDK ready to use.
});
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
When you use remote evaluation, user information is not stored on the device. Any user information already stored from local evaluation is deleted.

For the differences between the two modes and how to choose one, see the [Evaluation Mode](/en/development-guide/sdk/evaluation-mode.md) document.
{% endhint %}

#### Session Policy Configuration

You can configure session persistence and expiration conditions using the session policy.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val sessionPolicy = HackleSessionPolicy.builder()
    .persistCondition(HackleSessionPersistCondition.NULL_TO_USER_ID)
    .timeoutCondition(
        HackleSessionTimeoutCondition.builder()
            .millis(3600000)
            .onForeground(false)
            .onBackground(true)
            .onApplicationStateChange(true)
            .build()
    )
    .build()

val config = HackleConfig.builder()
    .sessionPolicy(sessionPolicy)
    .build()

Hackle.initialize(applicationContext, YOUR_APP_SDK_KEY, config) {
    // SDK ready to use.
}
```

{% endtab %}

{% tab title="Java" %}

```java
HackleSessionTimeoutCondition timeoutCondition = HackleSessionTimeoutCondition.builder()
    .millis(3600000)
    .onForeground(false)
    .onBackground(true)
    .onApplicationStateChange(true)
    .build();

HackleSessionPolicy sessionPolicy = HackleSessionPolicy.builder()
    .persistCondition(HackleSessionPersistCondition.NULL_TO_USER_ID)
    .timeoutCondition(timeoutCondition)
    .build();

HackleConfig config = HackleConfig.builder()
    .sessionPolicy(sessionPolicy)
    .build();

HackleApp.initializeApp(getApplicationContext(), YOUR_APP_SDK_KEY, config, () -> {
    // SDK ready to use.
});
```

{% endtab %}
{% endtabs %}

### Get Instance

After initialization, you can retrieve the `HackleApp` instance with the code below.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import io.hackle.android.Hackle
import io.hackle.android.app

val hackleApp = Hackle.app
```

{% endtab %}

{% tab title="Java" %}

```java
import io.hackle.android.HackleApp;

HackleApp hackleApp = HackleApp.getInstance();
```

{% endtab %}
{% endtabs %}

### Refresh Dashboard Configuration

After initialization, you can explicitly refresh Dashboard configuration when needed.

{% hint style="warning" %}
This function can only be called once every 60 seconds.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
hackleApp.fetch {
  // done
}
```

{% endtab %}

{% tab title="Java" %}

```java
hackleApp.fetch(new Runnable() {
    @Override
    public void run() {
        // done
    }
});
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.hackle.io/en/development-guide/android.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
