Blog Infos
Author
Published
Topics
Published
Topics

In this article, we will discuss about the need of a client-sever communication, the role of Server-Sent Events(SSEs), difference between SSEs and other communication mechanisms such as WebSockets and Long-Polling, and finally how we can implement SSEs in our Android app using the okhttp-sse Library.

Communicating with Servers in Android
1. Stateless communication b/w client-server (REST)

In REST architecture, the communication between the client and the server is a stateless ,i.e, every communication between the client and the server is like a new one. There is no information carried over from the previous communications. So every time the client needs a new or updated data, the client has to request fresh data from the server.

Even though it satisfies a lot of use-cases we come across while developing applications, such as fetching a list of items from server, posting data to a server, fetching subscription status etc, but it does not satisfies some other use cases such as chatting, fetching updated news feed every 1 minute, fetching stock prices in realtime(which gets updated almost every second). So we need some mechanism through which we maintain a 2 way connection between client and server to get the latest data in realtime. This is also called client pull way of handling data delivery.

2. Bi-directional communication using WebSockets

WebSocket is bidirectional, a full-duplex protocol that maintains a 2 way communication between client and server. This is also called server-push way of handling data delivery. It is a stateful protocol, which means the connection between client and server will keep alive until it is terminated by either the client or the server. After closing the connection by either of the client and server, the connection is terminated from both ends. This is suitable for use-cases where we are required to constantly listen for or post new data from and to the server. MQTT is yet another publish-subscribe mechanism used for 2-way communication especially in low-end or IOT devices, but that is a bigger topic so let us keep it out of scope of this article.

3. Unidirectional communication between client-sever

In WebSocket, we have a bi-directional communication between client and server, that is both client and server get new data from each other. However imagine use cases such as a facebook or twitter feed, showing realtime prices of stock prices or showing live cricket scores, if you closely observe, though having a WebSocket or a bi-directional communication may solve our use-cases, but actually client is not requesting anything from the server. Instead it is just the server which is publishing new data to our client in real-time. For such cases we have another protocol known as Server-Sent Events , popularly known as SSE. In Server-Sent event, the communication is carried out from server to browser only and browser cannot send any data to the server.

But why SSEs when we have WebSockets?

I am sure this question may have crossed your mind by now after reading the last point that why do I even need a unidirectional communication where only server can send an event/data to a client and not vice versa when we can directly have a 2 way communication using WebSockets? Trust me I had the same question and tried to find out to answer to this at different places. Fortunately, html5rocks.com have answered this beautifully in their article and I will quote.

SSEs are sent over traditional HTTP. That means they do not require a special protocol or server implementation to get working. WebSockets on the other hand, require full-duplex connections and new Web Socket servers to handle the protocol. In addition, Server-Sent Events have a variety of features that WebSockets lack by design such as automatic reconnection, event IDs, and the ability to send arbitrary events.

I hope after reading this you are clear on why and when to use Server-Sent Events in you app. Now let’s just quickly jump to the implementation part. Okhttp, a common Library we often use in Android apps for making HTTP requests, has an experimental support for server-sent events, and we will be using this to implement a server-sent event.

Server-Sent Events implementation in Android
Overview

As mentioned earlier, we will be using okhttp-sse Library for implementing server-sent events in our app. I have designed the API for the same and will share the project link at the end of the article. For implementation’s sake, we will be using the following endpoints in our app, these HTTP endpoints which support SSEs. This post endpoint will be used ( we will use POSTMAN) to send server events to the client.

POST: https://test-sse-backend.herokuapp.com/event
Body: {
"info": "start"
}

The client will listen on the following endpoint.

GET: https://test-sse-backend.herokuapp.com/events
Dependencies

As a first step, let’s add dependencies for Kotlin Coroutines for dealing with asynchronous tasks ,Okhttp for making network calls and Okhttp-SSE for Server-Sent Event support. Add the following lines to app level build.gradle

dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
// Okhttp
implementation "com.squareup.okhttp3:okhttp:4.9.3"
// Server Sent Events
implementation "com.squareup.okhttp3:okhttp-sse:4.9.3"
testImplementation "com.squareup.okhttp3:okhttp-sse:4.9.3"
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
}
view raw build.gradle hosted with ❤ by GitHub
EventSourceListener for SSE

As a next step, we will create our implementation for the EventSourceListener Interface. This interface contains methods onOpen(), onClose(), onEventReceived() and onFailure(). As name suggests, onOpen() and onClose() are triggered when a connection is established and connection is closed respectively. On the other hand onFailure() is called when there is some issue connecting with the server or connection is lost. What really is important for us is the method onEventReceived()

onEventReceived() is triggered every time a new event is triggered or sent from the backend!!

val eventSourceListener = object : EventSourceListener() {
override fun onOpen(eventSource: EventSource, response: Response) {
super.onOpen(eventSource, response)
Log.d(MainActivity.TAG, "Connection Opened")
}
override fun onClosed(eventSource: EventSource) {
super.onClosed(eventSource)
Log.d(MainActivity.TAG, "Connection Closed")
}
override fun onEvent(
eventSource: EventSource,
id: String?,
type: String?,
data: String
) {
super.onEvent(eventSource, id, type, data)
Log.d(MainActivity.TAG, "On Event Received! Data -: $data")
}
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
super.onFailure(eventSource, t, response)
Log.d(MainActivity.TAG, "On Failure -: ${response?.body}")
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

Job Offers

Job Offers


    Android Developer

    Small and Modern GmbH
    Hamburg, Remote (Germany)
    • Full Time
    apply now

    Android Build Engineer

    Pinterest
    San Francisco, CA | Seattle, WA
    • Full Time
    apply now

    Senior Android Developer (Remote)

    Komoot
    Europe
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Leveling Up Your Tests

We all know about TDD and Unit Testing, and even screenshot testing, but sometimes we do not need to embrace a new paradigm to make our tests better. These are several techniques I have adopted…
Watch Video

Leveling Up Your Tests

Jobs

OkHttp Client and EventSourceFactory

After creating an eventSourceListener, we need to create an okhttp client with a longer timeout. We set a longer timeout so that the connection does not close automatically some time after not getting any response from server, we need it to listen even though server event may still not have been triggered.

In the request of the client, we need to provide an additional header “text/event-stream” along with our API endpoint, which is used for Server-Sent Events. Btw the same header is also added in the Send Event API.

We also need to create an EventSourceFactory for our client to which we will provide our implementation for EventSourceListener interface.

val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.MINUTES)
.writeTimeout(10, TimeUnit.MINUTES)
.build()
val request = Request.Builder()
.url("https://test-sse-backend.herokuapp.com/events")
.header("Accept", "application/json; q=0.5")
.addHeader("Accept", "text/event-stream")
.build()
EventSources.createFactory(client)
.newEventSource(request = request, listener = eventSourceListener)
view raw MainActivity.kt hosted with ❤ by GitHub
Launch a coroutine and make the API call

Finally we will launch a new coroutine and execute our client request.

lifecycleScope.launchWhenCreated {
withContext(Dispatchers.IO) {
client.newCall(request).enqueue(responseCallback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e(MainActivity.TAG, "API Call Failure ${e.localizedMessage}")
}
override fun onResponse(call: Call, response: Response) {
Log.d(MainActivity.TAG, "APi Call Success ${response.body.toString()}")
}
})
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

That is it!! We have finally implemented Server-sent events in our Android App. Time to test it.

Here is the full code of MainActivity.kt

class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val eventSourceListener = object : EventSourceListener() {
override fun onOpen(eventSource: EventSource, response: Response) {
super.onOpen(eventSource, response)
Log.d(TAG, "Connection Opened")
}
override fun onClosed(eventSource: EventSource) {
super.onClosed(eventSource)
Log.d(TAG, "Connection Closed")
}
override fun onEvent(
eventSource: EventSource,
id: String?,
type: String?,
data: String
) {
super.onEvent(eventSource, id, type, data)
Log.d(TAG, "On Event Received! Data -: $data")
}
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
super.onFailure(eventSource, t, response)
Log.d(TAG, "On Failure -: ${response?.body}")
}
}
val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.MINUTES)
.writeTimeout(10, TimeUnit.MINUTES)
.build()
val request = Request.Builder()
.url("https://test-sse-backend.herokuapp.com/events")
.header("Accept", "application/json; q=0.5")
.addHeader("Accept", "text/event-stream")
.build()
EventSources.createFactory(client)
.newEventSource(request = request, listener = eventSourceListener)
lifecycleScope.launchWhenCreated {
withContext(Dispatchers.IO) {
client.newCall(request).enqueue(responseCallback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e(TAG, "API Call Failure ${e.localizedMessage}")
}
override fun onResponse(call: Call, response: Response) {
Log.d(TAG, "APi Call Success ${response.body.toString()}")
}
})
}
}
}
}
view raw MainActvity.kt hosted with ❤ by GitHub

I am attaching a video/gif to show how our app automatically listens to the events whenever a new one is triggered from the server(we will use Postman). Hope this gives a gist about the usage of SSE in Android.

GitHub link for SSE Backend Project -: https://github.com/hiteshchopra11/Server-Sent-Events-Backend

Thanks for spending time to read my article. I hope you learnt something new today. Please like and share this article with your friends and do let me know in the comments if you liked the article or anything else you want me to cover in this article.

This article was originally published on proandroiddev.com on April 23, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Collections are a set of interfaces and classes that implement highly optimised data structures.…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu