Home
/
Chat
/
Android
/
Local caching

Message collection

A MessageCollection allows you to swiftly create a chat view that includes all data. This page explains how to make a view using the collection.

Note: Understand the differences between local caching and SyncManager, and learn how to migrate Message collection.


Create a collection

First, create a MessageCollection through the createMessageCollection() method. Here, you need to set a MessageListParams instance to determine the message order and the starting point of the message list in the chat view.

private void createMessageCollection() {
    // Create a MessageListParams to be used in the MessageCollection.
    MessageListParams params = new MessageListParams();
    params.setReverse(false);
    // You can add other query setters.

    final MessageCollection collection = new MessageCollection.Builder(groupChannel, params)
        .setStartingPoint(startingPoint)
        .build();

Starting point

The startingPoint is the reference point of the message retrieval for a chat view. This should be specified as a timestamp.

If you set the value of startingPoint to the current time or Long.MAX_VALUE, messages in the view will be retrieved in reverse chronological order, showing messages from the most recent to the oldest.

If you set the value of startingPoint to the message last read by the current user, messages in the view will be fetched in chronological order, starting from the last message seen by the user to the latest.


Initialization

After creating a MessageCollection, initialize the instance through the initialize() method. Here, you need to set the MessageCollectionInitPolicy.

Policy

The MessageCollectionInitPolicy determines how initialization deals with the message data retrieved from the local cache and API calls. Because we only support InitPolicy.CACHE_AND_REPLACE_BY_API at this time, an additional implementation is required to clear the messages in the local cache before adding the messages from the remote server to it.

PolicyDescription

CACHE_AND_REPLACE_BY_API

Retrieves cached messages from the local cache and then replaces them with the messages pulled from Sendbird server through API calls.

// Initialize messages from the startingPoint.
private void initialize() {
    collection.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API, new MessageCollectionInitHandler() {
        @Override
        public void onCacheResult(@Nullable List<BaseMessage> cachedList, @Nullable SendBirdException e) {
            // Messages will be retrieved from the local cache.
            // They can be outdated or far from the startingPoint.
        }

        @Override
        public void onApiResult(@Nullable List<BaseMessage> apiResultList, @Nullable SendBirdException e) {
            // Messages will be retrieved through API calls from Sendbird server.
            // According to the MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API,
            // the existing data source needs to be cleared
            // before adding retrieved messages to the local cache.
        }
    });
}

Pagination

Use the loadPrevious() and loadNext() methods to retrieve messages from the previous page and the next page.

When the loadPrevious() method is called, the Chat SDK first checks whether there're more messages to load from the previous page through the hasPrevious(). The same goes for the loadNext().

These methods have to be called during initialization as well.

Load previous messagesLoad next messages
// Load the previous messages when the scroll reaches the first message in the chat view.
private void loadPrevious() {
    if (collection.hasPrevious()) {
        collection.loadPrevious(new BaseChannel.GetMessagesHandler() {
            @Override
            public void onResult(List<BaseMessage> messages, SendBirdException e) {
                if (e != null) {
                    // Handle error.
                    return;
                }
            }
        });
    }
}

Message events

Use the setMessageCollectionHandler to determine how the client app would react to message-related events.

The following table shows possible cases where each event handler can be called.

HandlerCalled when

onMessageAdded()

- A new message is created as a real-time event.
- New messages are fetched during changelog sync.

onMessageDeleted()

- A message is deleted as a real-time event.
- Message deletion is detected during changelog sync.
- The value of the MessageListParams setter such as custom_type changes.

onMessageUpdated()

- A message is updated as a real-time event.
- Message update is detected during changelog sync.
- The sending status of a pending message changes.

onChannelUpdated()

- The channel information that is included in the user's current chat view is updated as a real-time event.
-Channel info update is detected during changelog sync.

onChannelDeleted()

- The current channel is deleted as a rea-time event.
- Channel deletion is detected during changelog sync.
* In both cases, the entire view should be disposed.

onHugeGapDetected()

- A huge gap is detected through Background sync. In this case, you need to dispose of the view and create a new MessageCollection instance.

collection.setMessageCollectionHandler(new MessageCollectionHandler() {
    @Override
    public void onMessagesAdded(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List<BaseMessage> messages) {
        switch (context.getMessagesSendingStatus()) {
            case SUCCEEDED:
                // Add messages to your data source.
                break;
            case PENDING:
                // Add pending messages to your data source.
                break;
        }
    }

    @Override
    public void onMessagesUpdated(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List<BaseMessage> messages) {
        switch (context.getMessagesSendingStatus()) {
            case SUCCEEDED:
                // Update the messages status of sent messages.
                break;
            case PENDING: (failed -> pending)
                // Update the message status from failed to pending in your data source.
                // Remove the failed message from the data source.
                break;
            case FAILED: (pending -> failed)
                // Update the message status from pending to failed in your data source.
                // Remove the pending message from the data source.
                break;
            case CANCELED: (pending -> canceled)
                // Remove the pending message from the data source.
                // Implement codes to process canceled messages on your end. The Chat SDK doesn't store canceled messages.
                break;
        }
    }

    @Override
    public void onMessagesDeleted(@NonNull MessageContext context, @NonNull GroupChannel channel, @NonNull List<BaseMessage> messages) {
        switch (context.getMessagesSendingStatus()) {
            case SUCCEEDED:
                // Remove the sent message from your data source.
                break;
            case FAILED:
                // Remove the failed message from your data source.
                break;
        }
    }

    @Override
    public void onChannelUpdated(@NonNull GroupChannelContext context, @NonNull GroupChannel channel) {
        // Change the chat view with the updated channel information.
    }

    @Override
    public void onChannelDeleted(@NonNull GroupChannelContext context, @NonNull String channelUrl) {
        // This is called when a channel is deleted. So the current chat view should be cleared.
    }

    @Override
    public void onHugeGapDetected() {
        // The Chat SDK detects more than 300 messages missing.

        // Clear the collection.
        collection.dispose();

        // Create a new message collection object.
        createMessageCollection();

        // An additional implementation is required for initialization.
    }
});

Gap

Depending on the connection status, the client app could miss message events. Especially when new messages or new channels stored in Sendbird server don't reach the Chat SDK in the client app, a gap is created. A gap is a chunk of objects that exist in the remote server but missing in the local cache.

Small gaps can be filled in through changelog sync. However, if the client app has been disconnected for a while, too big of a gap can be created.

Huge gap

A gap can be created for many reasons, such as when a user has been offline for days. When the client app is back online, the Chat SDK checks with Sendbird server to see if there are any new messages. If it detects more than 300 messages missing in the local cache compared to the remote server, the SDK determines that there is a huge gap and the onHugeGapDetected() is called.

Note: To adjust the number, see this page to learn how to contact support.

In this case, the existing MessageCollection should be cleared through the dispose() method and a new one must be created for better performance.

private void dispose() {
    collection.dispose();
}

Dispose of the collection

The dispose() should be called when you need to clear the current chat view. For example, this can be used when the current user leaves the channel or when a new collection has to be created due to a huge gap detected by the onHugeGapDetected().

private void dispose() {
    collection.dispose();
}