# Android Introduction

The Android SDK enables you to build any type of chat or messaging experience for Android. It consists of four major components:

- **Client** : A low-level client which provides the main chat functionality. It manages the user connection and events, and exposes all the API endpoints.

- **State** : The state library manages the in-memory state of the SDK and is used by the UI components. It exposes `StateFlow` objects that make it easy to build your own UI.

- **Offline** : The offline library provides persistent storage, optimistic UI updates, caching channels and messages and allows limited usage of the SDK while offline.

- **UI** : The UI libraries provide fully functional UI components, such as channel and message lists.

We typically recommend building a prototype with the full UI package. This is the fastest way to ensure that you have the integration set up correctly in combination with the backend and other teams. After that, about half of our customers launch in production with our UI package.

There are some limitations to Android UI reusability, so if you have more complicated design needs, you'll want to drop down a level to the _offline_ package. For most applications, you won't have to use the client directly (note that it's a lot of work to keep the chat state updated manually, which you'd have to do when using the client).

Before going through these docs, we recommend trying out the [Android Chat tutorial](https://getstream.io/tutorials/android-chat/#kotlin) and taking a look at the [Android Chat Sample App](https://github.com/GetStream/stream-chat-android#sample-app). You can also check out our [API docs](https://getstream.github.io/stream-chat-android/) for more information, as well our source code [on GitHub](https://github.com/GetStream/stream-chat-android).

The [online API tour](https://getstream.io/chat/get_started/) is also a nice way to learn about how the API works. It's in-browser, and therefore Javascript based, but the ideas are pretty much the same in Kotlin.

## Getting Started

This guide quickly brings you up to speed on [Stream’s Chat API](https://getstream.io/chat/). The API is flexible and allows you to build any type of chat or messaging. Note that if you build on [our UI components](https://github.com/GetStream/stream-chat-android/tree/main/stream-chat-android-ui-components), you won't need to call into this API too often after the initialization of the library.

To include the Stream Chat SDK in your Android project, add the following to your `build.gradle` file:

<codetabs>

<codetabs-item value="groovy" label="Groovy">

```groovy
repositories {
  google()
  mavenCentral()
  maven { url "https://jitpack.io" }
}

dependencies {
  // Client + state + XML UI components
  implementation "io.getstream:stream-chat-android-ui-components:$stream_version"
  // Client + state + Jetpack Compose UI components
  implementation "io.getstream:stream-chat-android-compose:$stream_version"
  // Client + state
  implementation "io.getstream:stream-chat-android-state:$stream_version"
  // Client only
  implementation "io.getstream:stream-chat-android-client:$stream_version"

  // Additional offline support
  implementation "io.getstream:stream-chat-android-offline:$stream_version"
}
```

</codetabs-item>

</codetabs>

For the latest version, check our [GitHub releases](https://github.com/GetStream/stream-chat-android/releases) page.

### Chat Client

Let's get started by initializing the client and connecting the current user:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val apiKey = "{{ api_key }}"
val token = "{{ chat_user_token }}"

// Step 1 - Set up the StatePlugin for in-memory state management
val statePluginFactory = StreamStatePluginFactory(
  config = StatePluginConfig(
    backgroundSyncEnabled = false,
    userPresence = true
  ),
  appContext = applicationContext,
)

// Step 2 - Set up the OfflinePlugin for offline storage
val offlinePluginFactory = StreamOfflinePluginFactory(applicationContext)

// Step 3 - Set up the client, together with the state/offline plugins
val client = ChatClient.Builder(apiKey, applicationContext)
  // Change log level
  .logLevel(ChatLogLevel.ALL)
  .withPlugins(statePluginFactory, offlinePluginFactory)
  .build()

// Step 4 - Authenticate and connect the user
val user = User(
  id = "summer-brook-2",
  name = "Paranoid Android",
  image = "https://bit.ly/2TIt8NR",
)

client.connectUser(
  user = user,
  token = token, // or client.devToken(userId); if auth is disabled for your app
).enqueue { result ->
  when (result) {
    is Result.Success -> { /* Handle success */ }
    is Result.Failure -> { /* Handle failure */ }
  }
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
String apiKey = "{{ api_key }}";
String token = "{{ chat_user_token }}";

// Step 1 - Set up the StatePlugin for in-memory state management
StreamStatePluginFactory statePluginFactory = new StreamStatePluginFactory(
  new StatePluginConfig(true, true),
  applicationContext
);

// Step 2 - Set up the OfflinePlugin for offline storage
StreamOfflinePluginFactory offlinePluginFactory = new StreamOfflinePluginFactory(applicationContext);

// Step 3 - Set up the client, together with the state/offline plugins
ChatClient client = new ChatClient.Builder(apiKey, applicationContext)
    // Change log level
    .logLevel(ChatLogLevel.ALL)
    .withPlugins(offlinePluginFactory, statePluginFactory)
    .build();

// Step 4 - Authenticate and connect the user
User user = new User.Builder()
    .withId("summer-brook-2")
    .withName("Paranoid Android")
    .withImage("https://bit.ly/2TIt8NR")
    .build();

client.connectUser(user, token).enqueue((result) -> {
  if (result.isSuccess()) {
    // Handle success
  } else {
    // Handler error
  }
});
```

</codetabs-item>

</codetabs>

The user token is typically provided by your backend when you login or register in the app. If authentication is disabled for your app, you can also use a `ChatClient#devToken` to generate an insecure token for development. Of course, you should never launch into production with authentication disabled.

For more complex token generation and expiration examples, have a look at [Token Expiration](/chat/docs/android/tokens_and_authentication/).

### Channels

Let’s continue by creating your first channel. A channel contains messages, a list of members who are permanently associated with the channel, and a list of watchers currently watching the channel. The example below shows how to set up a channel:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = client.channel(channelType = "messaging", channelId = "travel")

val extraData = mutableMapOf<String, Any>(
  "name" to "Awesome channel about traveling"
)

// Creating a channel with the low level client
channelClient.create(memberIds = emptyList(), extraData = extraData).enqueue { result ->
  if (result is Result.Success) {
    val channel: Channel = result.value
    // Use channel by calling methods on channelClient
  } else {
    // Handle Result.Failure
  }
}

// Watching a channel's state using the state library
scope.launch {
  client.watchChannelAsState(cid = "messaging:travel", messageLimit = 0).collect { channelState ->
    if (channelState != null) {
      // StateFlow objects to observe
      channelState.messages
      channelState.reads
      channelState.typing
    } else {
      // User not connected yet.
    }
  }
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
ChannelClient channelClient = client.channel("messaging", "travel");

Map<String, Object> extraData = new HashMap<>();
extraData.put("name", "Awesome channel about traveling");
List<String> memberIds = new ArrayList<String>();

// Creating a channel with the low level client
channelClient.create(memberIds, extraData).enqueue((result) -> {
  if (result.isSuccess()) {
    Channel channel = result.getOrNull();
    // Use channel by calling methods on channelClient
  } else {
    // Handle error
  }
});

// Watching a channel's state using the state library
StateFlow<ChannelState> channelStateFlow = ChatClientExtensions.watchChannelAsState(client, "messaging:travel", 0);
LiveData<ChannelState> channelStateLiveData = FlowExtensions.asLiveData(channelStateFlow);

channelStateLiveData.observe(lifecycleOwner, channelState -> {
  if (channelState != null) {
    // StateFlow objects to observe. Use FlowExtensions.asLiveData(stateFlow); to LiveData conversion.
    channelState.getMessages();
    channelState.getReads();
    channelState.getTyping();
  } else {
    // User not connected yet.
  }
});
```

</codetabs-item>

</codetabs>

The first two arguments are the channel type and the channel ID ( `messaging`  and  `travel`  in this case). In this case, the channel ID is explicit, but channels can also be created by specifying a list of members, in that case, an ID will be generated automatically.

The channel type controls the settings we’re using for this channel. There are four default types of channels:

- livestream
- messaging
- team
- commerce

These options provide you with the most sensible defaults for the given use cases. You can also define custom channel types if the defaults don’t work for your use case. See [Channel Types](/chat/docs/android/channel_features/) for more info.

The `extraData` argument is an object containing custom channel data. You can add as many custom fields as you would like, as long as the total size of the object is _less than 5KB_. This applies not only to channels, but also messages, users, attachments, and reactions.

### Messages

Now that we have the channel set up, let's send our first chat message:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val message = Message(
  text = "I’m mowing the air Rand, I’m mowing the air.",
  cid = "messaging:travel",
  extraData = mutableMapOf("customField" to "123")
)

channelClient.sendMessage(message).enqueue { result ->
  if (result is Result.Success) {
    val message: Message = result.value
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
Map<String, Object> extraData = new HashMap<>();
extraData.put("customField", "123");

Message message = new Message.Builder()
    .withText("I’m mowing the air Rand, I’m mowing the air.")
    .withCid("messaging:travel")
    .withExtraData(extraData)
    .build();

channelClient.sendMessage(message).enqueue((result) -> {
  if (result.isSuccess()) {
    Message sentMessage = result.getOrNull();
  } else {
    // Handle error
  }
});
```

</codetabs-item>

</codetabs>

When you send a message to a channel, Stream Chat automatically broadcasts to all the people that are watching this channel and updates in real-time.

### Querying Channels

The client.queryChannels method enables you to retrieve a list of channels. You can specify a filter and sort order. The offline support library keeps the channels list updated as new messages, reactions and new channels arrive.

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val filter = Filters.and(
  Filters.eq("type", "messaging"),
  Filters.`in`("members", "john"),
)
val sort = QuerySortByField<Channel>().descByName("lastMessageAt")

val request = QueryChannelsRequest(
  filter = filter,
  offset = 0,
  limit = 10,
  querySort = sort
).withWatch().withState()

client.queryChannels(request).enqueue { result ->
  if (result is Result.Success) {
    val channels: List<Channel> = result.value
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
FilterObject filter = Filters.and(Filters.eq("type", "messaging"), Filters.in("members", "john"));
QuerySorter<Channel> sort = QuerySortByField.descByName("lastMessageAt");

int offset = 0;
int limit = 10;
int messageLimit = 0;
int memberLimit = 0;
QueryChannelsRequest request = new QueryChannelsRequest(filter, offset, limit, sort, messageLimit, memberLimit).withWatch().withState();

client.queryChannels(request).enqueue((result) -> {
  if (result.isSuccess()) {
    List<Channel> channels = result.getOrNull();
  } else {
    // Handle error
  }
});
```

</codetabs-item>

</codetabs>

To learn more about which fields you can query and sort on have a look at the [query channels](/chat/docs/android/query_channels/) documentation.

## Conclusion

Now that you understand the building blocks of a fully functional chat integration, let’s move on to the next sections of the documentation, where we dive deeper into details on each API endpoint.


---

This page was last updated at 2026-03-13T13:15:44.776Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/android/](https://getstream.io/chat/docs/android/).