Reactive Programming in Trendyol Android App Part I

Ali Gökdemir
Trendyol Tech
Published in
6 min readJun 12, 2021

Hello everyone, in this article of Trendyol Tech, we’re going to take a look at one of the hottest topics of mobile development, called Reactive Programming. We will explore how we use and implement one of the strongest paradigms of programming for mobile development. Before diving into the details, let us first see what reactive programming is and why we needed it in the first place. The outline of the article will be as follows:

  1. Reactive Programming Paradigm
  2. Why RxJava?
  3. Trendyol Way of Reactivity

Reactive Programming Paradigm

Let’s start with the question of why we need reactive programming in the first place? In the most direct way, for the sake of providing responsive i.e. smoother user experience. When we look at the word reactive, the simplest definition for that is tending to react to changes occurring in a specific period of time. In programming, that change occurs through data and consists of streams. So, we can say that reactive programming aims to provide data flows with respect to changing parameters, requests, etc. In reactive programming, a task such as fetching user info from an endpoint is completed through data flows emitted by components. With this in mind, we can say that there are two main actors in reactive programming, namely observer and observable. Intuitively, observer consumes the data while observable provides (emits) the data for the consumption. Seems simple? Not completely! All of the observing and emitting operations require concurrency and this is where all the frameworks and libraries come into play. All of the libraries’ aim out there is to provide this structure in the simplest way. In this article, we’re going to take a look at RxJava which is also the core of reactive programming in Trendyol Android team at the time of writing this article.

Why RxJava

We need to take a look at what RxJava actually is before answering why we’re using RxJava. In its GitHub repository, the founders define it as follows:

RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.

It extends the observer pattern to support sequences of data/events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety and concurrent data structures.

It’s basically a framework developed for reactive programming. But why is it so popular?

  1. Provides smooth UI/UX by making responsive apps

RxJava is created to support a variety of data streams and events which means it is able to perform tasks concurrently. These tasks can be processing complex data in the background and also sending a request to an endpoint. While doing so, it is also required to do all of the operations’ error handlings and pass the result to the main thread without causing any decrease in the responsiveness of an application.

2. No more dealing with callbacks

In the Android world, the old way of asynchronous programming was to use AsyncTasks and Handlers which relies on callbacks and therefore was a potential problem source because of leaks and other kinds of problems. Also, it was a nightmare to read and modify the code with all of the methods inside each other. RxJava makes it easier for reading, writing, and debugging asynchronous programs.

3. Error handling at its finest

Error handling for traditional AsyncTask was also a problem that should be handled manually by the developer while also dealing with OnPostExecute to reflect those changes to UI. But in RxJava, we have to deal with them in one place. RxJava provides us methods that let us know if an operation is performed, completed, or had problems when being performed. The stream has onNext() method which consumes elements emitted. If we had problems in onNext(), then onError() method is the place to handle all these problems.

4. A lot of operators to perform what you need

RxJava provides hundreds of operators that can handle your problem and modify the data in the way you need. Therefore, you don’t need any other library or framework to do all of them for you. This gives developers the flexibility they need when developing applications.

5. Support for Android’s lifecycle and other libraries

RxJava has a specific library called RxLifecycle in order to meet Android’s lifecycle needs which also makes it even more popular in the Android world. Also, it has great integration with other popular Android libraries such as Retrofit, one of the most popular networking libraries in Android.

Reasons of using RxJava can go on and on but we’ll also take a look at how we use RxJava in Trendyol Android App. So, let’s continue with Trendyol Way.

Trendyol Way of Reactivity

As Trendyol Android team, we use RxJava in many operations throughout the application. Some of these are sending requests to the backend, fetching information from the local database and also to perform some heavy operations that may affect the application performance which can be initializing an SDK or the application itself. Because Trendyol is a heavily dynamic app, we take the application configuration from the backend which gives us more flexibility. Also, we have some fire and forget methods that we run in the background thread like sending events. For all of these RxJava comes into play.

As an example of initialization we fetch data for the configuration of the app as following:

service
.fetchConfiguration()
.subscribeOn(Schedulers.io())
analyticsViewModel.reportAsync(
Observable.fromIterable(listOfItems)
.map {
ImpressionEvent(
listOfItems,
impressionDataProvider.getEvent(it)
)
}
)

At the code above, you see that we have mapping inside reportAsync method which maps a list of items to ImpressionEvents, and below there is the continuation of the operation above.

For event sending, at the core of this mechanism we make use of RxJava as following:

fun reportAsync(events: Observable<out Event>): Disposable = events
.subscribeOn(Schedulers.computation())
.subscribe({ report(it) }, { Reporter.report(it) })

Inside the subscribe parentheses, we have functions to tell RxJava what to do in onSuccess and onError. As seen from above, when reportAsync method is called and events are given to the method as observable streams when those mappings are successful, we report them by calling report method, and when we have an exception during the operation of mapping, we report it using our Reporter class.

Sometimes, RxJava operators or existing extension functions do not cover our needs, so we need to write the extension function that’ll fit our needs perfectly. In the next section, we’ll take a look at the extension functions we wrote.

In Trendyol Android team, we follow the best practices recommended by Google. Therefore, we’re using the famous Resource class for wrapping our data to better abstract networking states. For the given Resource class, we need a method that is going to map the data with the given mapper function from outside without overriding the resource’s properties like error and status and return it as Observable.

fun <T, R> Observable<Resource<T>>.mapOnData(mapper: (T) -> R): Observable<Resource<R>> {
return this.map {
Resource(it.status, it.data?.let(mapper), it.error)
}
}

This way, the mapper is only responsible for mapping an input to the desired output form. Wrapping it with observable is extensions function’s job. The consumer side of this extensions function is like below:

fun fetchReviewRatingCriteria(): Observable<Resource<ReviewRatingCriteria>> =
repository
.fetchCriteriaForPublication()
.mapOnData {reviewRatingCriteriaMapper.mapFromResponse(it)}

One of the other scenarios where we needed an extension function was when a network request failed and we have local items to be displayed on the screen. We’ve got that covered by the extension function below.

fun <T, R> Observable<Resource<T>>.mapOnError(
mapper: (T) -> R,
onError: (Throwable) -> R
): Observable<Resource<R>> {
return this.map { incomingResource ->
when (incomingResource) {
is Resource.Success -> Resource.Success(mapper.invoke(incomingResource.data))
is Resource.Error -> Resource.Success(onError.invoke(incomingResource.exception))
is Resource.Loading -> Resource.Loading()
}
}
}

Thanks to the function above, when the request is failed, we are able to smoothly display the local items we have to the user when needed. The part that uses this extension function implements it like below:

operator fun invoke(): Observable<Resource<List<ViewState>>> {
return fetchUseCase.fetch()
.mapOnError(
mapper = { list -> list.listItems.map{ViewState(it)} },
onError = { getLocalItemsUseCase() }
)
}

There are more extension functions that we can mention, but in order not to make this article longer, we’ll keep them for later. Now, let us move on with the future of reactivity in Trendyol Android team.

Thanks for reading the article, if you have any questions or comments regarding the article or our practices in general in Trendyol Android team, you’re more than welcome to ask.

--

--