Courier Android Library

repository·main·Indexed 41 days ago

https://github.com/gojek/courier-android

A Kotlin library for establishing long-running, bi-directional MQTT connections on Android. It optimizes for battery and data efficiency using adaptive keep-alive and automatic reconnection. Courier features a service-oriented approach using annotations like @Receive and @Send, and provides multiple adapters for serialization (Gson, Moshi, Kotlin Serialization, Protobuf) and streaming (RxJava2, Kotlin Coroutines Flow/Channels).

Tokens
17.1K
Snippets
52
Records
79
Agent score
99%

What's inside Courier

  1. What is Courier

    main
    Courier is a Kotlin library designed to create and manage long-running connections using the MQTT protocol. It facilitates persistent, bi-directional communication between a client and a server, optimized for mobile devices by using keep-alive packets to minimize battery and data consumption.
  2. Key features of the Courier library

    main

    Courier provides several features for robust mobile connectivity:

    • Adaptive Keep Alive: Optimizes connection maintenance.
    • Automatic Reconnect & Resubscribe: Handles connection drops gracefully.
    • Subscription Store & Database Persistence: Ensures subscriptions and state are maintained.
    • Backpressure handling: Manages data flow to prevent overwhelming the client.
    • Reliable Pinging: Uses Alarm, Timer, and WorkManager to send pings.
    • Extensibility: Supports Message & Stream Adapters and MQTT Chuck.
  3. Inspect MQTT packets with MQTT Chuck

    main
    MQTT Chuck is a debugging tool used to inspect all outgoing and incoming MQTT packets for an underlying MQTT connection. It functions similarly to HTTP Chuck for Android, using an interceptor to capture, persist, and provide a UI for viewing MQTT traffic. It includes features for searching, sharing, and clearing packet data.
  4. What is an Authenticator in Courier

    main

    In Courier, an Authenticator is an interface used to manage MQTT connection credentials. When an MQTT client attempts to connect to a broker, it sends a username and password within the CONNECT packet. If the broker rejects these credentials (returning reason code 5), the Courier library uses the Authenticator to refresh the connection options (username and password) to enable a successful reconnection.

    You can either provide your own custom implementation of the Authenticator interface or use the library's built-in HttpAuthenticator.

  5. What is a Ping Sender in Courier Android

    main

    MQTT connections can be terminated by TCP binding timeouts if they remain idle for too long. To prevent this, the client must send PINGREQ packets at a specific Keepalive Interval. The broker responds with a PINGRESP packet to confirm the connection is alive.

    In the Courier Android library, a Ping Sender is an abstraction that encapsulates the mechanism used to send these ping requests. Different implementations are available depending on whether you need to maintain the connection while the app is in the background or foreground.

  6. Split placeholder values in MQTT topics

    main

    You can extract specific parts of a placeholder value using a delimiter and a 0-based index with the syntax (<placeholder>,<delimiter>,<index>).

    Example: If the client id (%c) is region:tenant:device:

    • Topic: user/(%c,:,2)/send
    • Logic: Splits region:tenant:device by : into [region, tenant, device] and picks index 2.
    • Resolved Topic: user/device/send.

    Warning: An index that is out of range will result in an IllegalArgumentException.

    @Send(topic = "user/(%c,:,2)/send", qos = QoS.ONE)
    fun send(@Data message: Message)
  7. Use Connection Placeholders in MQTT Topics

    main

    Courier supports connection placeholders that are automatically resolved from the established MQTT connection. These can be used in @Send, @Subscribe, @SubscribeMultiple, and @Unsubscribe annotations.

    Supported Placeholders:

    • %u: The connection's username.
    • %c: The connection's client id.

    Important Limitation: Placeholders are not resolved for @Receive topics. For @Receive, the topic string is used as-is. If you need placeholder-based receiving, use @Subscribe with a stream instead.

    interface MessageService {
    	@Send(topic = "user/%u/send", qos = QoS.ONE)
    	fun send(@Data message: Message)
    
    	@Subscribe(topic = "client/%c/receive", qos = QoS.ONE)
    	fun subscribe(): Observable<Message>
    }
  8. MQTT Quality of Service (QoS) levels in Courier

    main

    Courier utilizes MQTT's built-in QoS levels to manage reliable message delivery. You can choose from three levels depending on your requirements for delivery guarantees:

    • QoS 0 (At most once): Fire and forget. The message is sent once without acknowledgment from the client or broker.
    • QoS 1 (At least once): Acknowledged delivery. The sender retries the message until an acknowledgment is received.
    • QoS 2 (Exactly once): Assured delivery. Uses a two-level handshake between sender and receiver to ensure exactly one copy of the message is delivered.
  9. Understand Subscription Store implementations

    main

    The Courier library uses a SubscriptionStore to manage current subscriptions and pending unsubscribe requests. This ensures that the client's subscription state is synchronized with the server, especially after reconnections.

    There are two available implementations:

    1. PersistableSubscriptionStore:

      • Subscriptions: Maintained in-memory.
      • Unsubscribe Requests: Maintained in SharedPreferences.
      • Behavior: Upon reconnection, the client automatically re-sends subscription packets and any pending unsubscribe packets. This is the more robust option for ensuring state consistency across app restarts or reconnections.
    2. InMemorySubscriptionStore:

      • Subscriptions: Maintained in-memory.
      • Unsubscribe Requests: Not maintained (lost if the process is killed).
      • Behavior: Upon reconnection, the client re-sends subscription packets, but does not re-send pending unsubscribe requests.
  10. Understand MQTT Quality of Service (QoS) levels

    main

    Quality of Service (QoS) defines the delivery guarantee for a message between a sender and a receiver. In MQTT, there are three standard levels:

    1. At most once (0): The message is sent without an acknowledgment. It may arrive once or not at all.
    2. At least once (1): The message is acknowledged with PUBACK. The receiver is guaranteed to receive the message, but it might receive it more than once if the acknowledgment is lost.
    3. Exactly once (2): The highest level of guarantee, ensuring the message is delivered exactly one time through a four-step handshake.

    When configuring QoS, consider both directions of delivery:

    • From the publishing client to the broker.
    • From the broker to the subscribing client.