Setup CI in GitLab for an Android project

CI – continuous integration – is a practice of automated implementation and distribution of the programmer’s work. CI has many advantages, and features, among which we would note the following:

  • Unified codebase, into which developers branches are being merged – achieved using GitFlow
  • Building automation – assembly starts automatically after new code is merged into master or develop branch using the pipeline in GitLab
  • Testing automation – automatically starts testing as a stage in the pipeline
  • Convenient access to build artifacts
  • Automated deploy – an additional script can publish artifacts in application stores or along the distribution path.

CI, like any automation, eliminates the human factor and automates repetitive actions. Therefore, it is desirable to configure CI on any serious project. And in the case when there are several developers and releases happens frequently, CI must be configured necessarily.
Typically, CI configuration is the responsibility of the DevOps engineer. However, although most companies have such a person, he may not be available or may not be able to configure CI specifically for the Android project. That is why Android developers should be able to do this on their own. How do we do it? I will tell and show in this article.

Let’s begin.
From GitLab point of view, CI consists of:

  • Jobs, which describes what to do. For example to build or to test the code
  • Stages, which describes when to start the job

Usually there are the following stages:
build job called compile
test – starts tests
staging – deploy on stage
production – deploy on prod

All this information about jobs and stages we need to tell GitLab. We will do it using .gitlab-ci file, which we need to put in the root of Android project. It will describe all the steps, as well as execute the necessary scripts.

At first we need to specify the image, which will perform the job

image: openjdk:8-jdk

Next, in the before_script section we’ll write the scripts that the server must execute in order to configure the build environment.

 - apt-get --quiet update --yes
 - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
 - wget --quiet${ANDROID_SDK_TOOLS}.zip
 - unzip -d android-sdk-linux
 - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
 - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
 - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
 - export ANDROID_HOME=$PWD/android-sdk-linux
 - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/

There is no magic here, and if you are familiar with unix systems and their commands, then you should understand everything: we just tell the system to download and install necessary packages.
Be sure to include the SDK version numbers that the server must install. To do this, add a variables block above before_script

 ANDROID_COMPILE_SDK: [version_number]
 ANDROID_BUILD_TOOLS: [version_number]
 ANDROID_SDK_TOOLS: [version_number]

Next, specify the types of assembly steps in the stages block

 - build
 - test
 - deploy

Now we are ready to complete the assembly itself.
I recommend to use two types of builds – release and debug. You can make as many options as you like based on your tasks.
At first specify debug build

 stage: build
 tags: [android]
 - develop
 - master
 - /^release.*$/
 - .gradle/caches
 VAR_NAME: BUILD_NUMBER // autoincrement build number

The only tag allows to trigger assembly in case of pushing to a specific branch or by tag, as we will see later.
The BUILD_NUMBER variable will be read using a token CI_PIPELINE_IID_TOKEN from Variables preferences in the CI block in the GitLab. It allows us to add an autoincrement build number to the version name. This approach is described in more detail here. And below I will show how to configure build.gradle to generate the version name and build number. In the meantime, continue with .gitlab-ci

Next block is the script

 # готовим переменную с номером сборки
 - GITLAB_URL=$(echo ${CI_PROJECT_URL} |awk -F "/" '{print $1 "//" $2$3}')
 - "VAR=$(curl -s -f --header \"PRIVATE-TOKEN: ${TOKEN}\" \"${GITLAB_URL}/api/v4/projects/${CI_PROJECT_ID}/variables/${VAR_NAME}\" | jq -r '.value' ) "
 - let VAR=VAR+1
 - "curl -s -f --request PUT --header \"PRIVATE-TOKEN: ${TOKEN}\" \"${GITLAB_URL}/api/v4/projects/${CI_PROJECT_ID}/variables/${VAR_NAME}\" --form \"value=${VAR}\" "
 # записываем переменную во временный файл, из которого будет считывать этот номер в build.gradle
 - echo $VAR > build.version
 - chmod +wx build.version
 - sed -i 's/android.enableBuildCache=false/android.enableBuildCache=true/g'
 # и собственно собираем сборку
 - ./gradlew clean app:assembleDebug

Now it remains only to specify how and where to store artifacts, i.e. apk::

 when: always
 expire_in: 4 weeks
 - app/build/outputs/apk/

Вот и все, а для релизной сборки укажем такой же блок, но stage и него будет deploy, а сборка будет собираться еще и с app:assembleRelease, и собираться такая сборка будет только при создании тэга в GitLab.

That’s all, and for the release build we will specify the same whole block, but stage will be deploy, and the assembly will also be built with app:assembleRelease, and such assembly will be triggered  only by tag in GitLab.

In addition, we need to configure automatic increment of build numbers.
To do so add next lines in build.gradle, which will generate versionCode and versionName based on the minimum version of the target api:

private Integer generateVersionCode() {
 def minSDK = rootProject.minSdkVersion * 1000000
 def major = rootProject.versionMajor * 10000
 def minor = rootProject.versionMinor * 100
 def patch = rootProject.versionPatch
 def versionCode = minSDK + major + minor + patch
 project.logger.debug('versonCode ', versionCode)
 return versionCode

private String generateVersionName() {
 String versionName = "${rootProject.versionMajor}.${rootProject.versionMinor}.${rootProject.versionPatch}"
 versionName += '.' + getBuildNumberFromFile()
 return versionName

Off course, in the root build.gradle you should have version numbers written in the form

 versionMajor = 1
 versionMinor = 0
 versionPatch = 0
 minSdkVersion = 23

Also add functions to get the build number from CI GitLab, and in the case of a local build – from git:

def getBuildNumberFromGit() {

try {
 def stdout = new ByteArrayOutputStream()
 exec {
 commandLine 'git', 'rev-list', '--all', '--count'
 standardOutput = stdout
 return stdout.toString().trim()
 catch (ignored) {
 return '?'

// This method gets the build number from the special file that we generated above in the .gitlab-ci script
def getBuildNumberFromFile() {

File versionFile = file('../build.version')
 if (versionFile.exists()) {
   return versionFile.readLines().get(0).trim()
 } else {
   return getBuildNumberFromGit()

It remains to call this method for the version name, as well as for the build number

defaultConfig {
 applicationId "ru.andreyaleev"
 minSdkVersion rootProject.minSdkVersion
 targetSdkVersion rootProject.targetSdkVersion
 versionName generateVersionName()

buildTypes {

applicationVariants.all { variant ->
 variant.outputs.each { output ->
   output.versionCodeOverride = generateVersionCode()
   output.outputFileName = "$setup.applicationId-${variant.versionName}.apk"

Done with the code, now you need to add the variable to GitLab, the idea is taken as described here

Go to Settings->CI/CD->Variables, and add BUILD_NUMBER and CI_PIPELIBE_ID_TOKEN

Set BUILD_NUMBER to 1, and into CI_PIPELIBE_ID_TOKEN put the value of the token that needs to be generated through the Access Tokens section of the profile

It remains to push the changes to GitLab, and it will automatically start Pipeline.
To create the release build that we configured above we need to create a Tag through Repository->Tags-> New Tag
Select the branch from which you want to create the assembly and run. All is ready!

RxJava. How to handle errors from multiple Observables?

Imagine you need to perform several parallel network request and you have Retrofit and Rx as instruments.
You may end up with zip or combineLatest operators:

    val firstObservable = ServerApi().firstRequest( requestParams1 )
    val secondObservable = ServerApi().secondRequest( requestParams2 )
    val thirdObservable = ServerApi().thirdRequest( requestParams3 )

    // combine observables into one
        Function3<FirstResponse, SecondResponse, ThirdResponse, CombinedResult> { firstResponse, secondResponse, thirdResponse ->
            // combine responses and create result for return function Function3
            CombinedResult( firstResponse, secondResponse, thirdResponse )
    .flatMap { combinedResult ->
        // perform some intermediate logic with combined result if needed
        Observable.just(combinedResult) // return original observable unchanged
    .subscribe({ combinedResult ->
        // end of request
        // show result
    }, {

There is a problem with this chain. When one of observables throw an exception, for example, HttpException, then the whole chain will be terminated and we would not be able to show combinedResult.
How to avoid this situation? We just need to handle errors correctly for every Observable in the combinable list.
There is a handy operator for this: onErrorReturn() Call it on the combineLatest parameters as follows:

            .onErrorReturn { responseFromException(it) },
            .onErrorReturn { responseFromException(it) },


onErrorReturn is a handy operator which emits new created object instead of terminating chain.

Use Faсtory Method pattern or just write a function called responseFromException(throwable: Throwable)) which will create an empty Response object with error for you:

    // FirstResponse, SecondResponse and ThirdResponse should inherit from BaseResponse
    fun <T : Any> responseFromException(throwable: Throwable): BaseResponse<T> {
        var errorResponse: BaseResponse<T> = BaseResponse()
        if (throwable is HttpException) {
            errorResponse.code = it.code()
            errorResponse.message = it.message()
      return errorResponse

That’s all. The rest code stay the same. With only one line added for each observable we now may expect the initial chain to work as expected: perform all request without terminal state.

Stateful and stateless widgets in Flutter

As Flutter is declarative user interface is being build as some function from state

UI = f(state)

In other words, UI observes State.
As Android Java and Kotlin developer I got used to write everything imperative. For example


or in Kotlin


In Flutter, on other hand, if you want to change UI, you need first to change the State.

A State can be defined as “whatever data you need in order to rebuild your UI at any moment in time”

Stateful and stateless widgets

In short Stateful are like var and stateless are like val variables in Koltin.
You cannot change A stateless widget. Use it when you need to show UI element once and never wont change it state. For example, text labels, icons, images etc.

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Stateless Widget',
        home: Scaffold(
          appBar: AppBar(
            title: Text('Appbar title'),
        body: Center(
          child: Text('static content'),

A Stateful widget can be changed in runtime. It may be input TextField, Slider, Checkbox etc.
A widget’s state is stored in a State object. When the widget’s state changes, the state object calls setState(), telling the framework to redraw the widget.

To declare StatefulWidget you need to extend StatefulWidget and create State for it

class MyWidget extends StatefulWidget {
  _MyWidgetState createState() => _MyWidgetState();

class _MyWidgetState extends State<MyWidget> {
  int _count = 42;
  // ···

Ephemeral state and App state

First, Application state. It used when you need to share data between screens, widgets or even sessions. In this kind of data you may want to store business logic data, for example, user preferences, downloaded lists, shopping cart etc.

Ephemeral state is a widget state. Examples: current page in PageView, current progress, current selected radioButton in radioGroup

Kodein: Inherit parent dependencies

When you override Kodein instance in activity, fragment or somewhere else, you will lose all dependencies from parent scope.
To prevent this, there is a extend method


In my App I have settings instance I want to use in Activity

class App : Application(), KodeinAware {

   override val kodein = Kodein {
     bind<ISettings>() with singleton { Settings(this@App) }

Now in activity:

class MainActivity : Activity(), KodeinAware {

    private val appKodein by kodein(App.instance)

    override val kodein = Kodein {
       // or
       // extend(appApplication.appKodein(), allowOverride = true)
       // now you have all dependecies, you initialized in App

    private val settings: ISettings by instance() // here we have it. Able to use settings instance

Dependency Injection with Kodein

If you build your Android application with Kotlin and want to implement DI technique then take a look at Kodein framework. Today I will take a quick look at it with you. I will not dive deep into dependency injection itself in this post. If you are not familiar with DI pattern, search articles on Dagger 2 for more info.
I will show Kodein practical usage on a simple example. Let’s say you want to inject singleton retrofit instance somewhere.

First, add Kodein dependency to the gradle file:

 implementation "org.kodein.di:kodein-di-generic-jvm:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-core:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-x:6.1.0"

Implement KodeinAware interface in your Application class. You will have to override kodein val and implement Retrofit instance there:

class App : Application(), KodeinAware {

    override val kodein = Kodein {
        bind<Retrofit>() with singleton {

All dependencies always starts with

 bind<TYPE>() with

followed by “singleton”, “provides” or “factory”
singleton” speaks for itself, “provides” generates new instance every time. If you need custom instance, then use “factory“; like”provides” it creates new instance every time but with applied constructor.

After declaring Dependency let’s use it!
You may need to inject Retrofit instance in Activity/Fragment or, if you use MVP pattern, in presenter. Difference is context: when in presenter you do not have context by default, you need to pass it. Let’s start with Activity case first.

Again, you need to imlpement KodeinAware interface and get the instance:

class MainActivity : AppCompatActivity, KodeinAware{

    override val kodein: Kodein by kodein()

    private val retrofit: Retrofit by instance()
    // use retrofit

Now, if you want to use retrofit in presenter or in some other class that is not aware of Android context,
first pass Context instance to get Kodein instance. I did it with App instance.
Add this into App class:

    override fun onCreate() {
        instance = this
    companion object {
        lateinit var instance: App
            private set

Now in presenter:

class MyPresenterImpl : IMyPresenter, KodeinAware {

    override val kodein by kodein(App.instance)

    override val kodeinContext = kcontext(App.instance)

    private val retrofit: Retrofit by instance()

    // use retrofit

For more info check out official documentation

Flowable from Room database

Hi! Today I’ll tell you how to get data from Room database in reactive way.

Before starting make sure you have following in your build.gradel:

// Room components
implementation "$rootProject.roomVersion"
implementation "$rootProject.roomVersion"
annotationProcessor "$rootProject.roomVersion"
androidTestImplementation "$rootProject.roomVersion"

// Lifecycle components
implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion"
implementation "android.arch.lifecycle:reactivestreams:$rootProject.archLifecycleVersion"
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"

// Rx
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.1'

First we need to get database from assets and define Dao class for data.

@Database(entities = {Verse.class}, version = 1, exportSchema = false)
public abstract class MyRoomDatabase extends RoomDatabase {
    public abstract MyDataDao myDataDao();

    private static MyRoomDatabase INSTANCE;

    static MyRoomDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (MyRoomDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            MyRoomDatabase.class, "data.sqlite3") // get db from assets
                            .openHelperFactory(new AssetSQLiteOpenHelperFactory())

        return INSTANCE;

    public void destroyInstance() {
        synchronized (MyRoomDatabase.class) {
            INSTANCE = null;

public interface MyDataDao {

    @Query("SELECT * from verses ORDER BY RANDOM() LIMIT 1")
    LiveData<Verse> getRandomVerse();

    @Query("SELECT * from verses ORDER BY RANDOM() LIMIT 1")
        // same request
    Flowable<Verse> getRxRandomVerse();

Above code represents raw request for random Verse objects from database table verses
What I like about Room is it allows us to get Rx Flowable as well as LiveData objects out of the box!
In our case we will use Flowable.
Next, define a repository class:

class MyRepository(context: Context?) {

    private val mDataDao: MyDataDao
    private val db: MyRoomDatabase? = MyRoomDatabase.getDatabase(context)

    init {
        mDataDao = db!!.myDataDao()

    fun getRxRandomVerse(): Flowable<Verse> {
        return mDataDao.getRxRandomVerse

    fun getDb(context: Context?): MyRoomDatabase? {
        return db ?: MyRoomDatabase.getDatabase(context)

    fun closeDb() {

Finally, let’s extract data onto presentation layer:

class MainActivityPresenter {

    private var mRepository: MyRepository? = null

    fun attach(mainActivityView: MainActivityView, context: Context) {
        this.mainActivityView = mainActivityView
        this.mRepository = MyRepository(context)

    fun getRandomVerse() {
                .subscribe({ verse ->
                }, { error ->

class MainActivity : MainActivityPresenter.MainActivityView {


    override fun updateUI(verse: String) {
        textViewVerse.text = verse


That’s all in general. Pretty simple and handy. I’ve successfully implemented this code in a new random quote app

If you have any questions, leave in comments below.

Adapt Android application for Wear OS

In March 2018 Google made Android Wear platform re-branding. New name – Wear OS. According to statistics, currently in the world of smart watches the leader is the Apple’s watchOS, taking 16,2 %, whereas Wear OS has 7%. However, the smart watch market is still new and we definitely can expect the number of users to growth. This means, we may create apps and watchfaces for wearable gadgets.
Let’s create Wear OS module for an existing Android app. I have just the right one – same old Workout Stopwatch.


Create module

Create new module in android studio project and choose wearable app

In build.gradle of created module add wearable dependecies:

dependencies {
    implementation ''
    implementation ''
    compileOnly ''

Wearable app may be in three states:

  • Completely independent of a phone app
  • Semi-independent (a phone app is not required and would provide only optional features)
  • Dependent on a phone app

For the first two, for independent ones, in Android Manifest add following for <application ../> tag:

 android:value="true" />

otherwise, the application will not be available to users with iPhone.
That is, if this flag is false, then the application can be installed only on the device connected to the phone on which the Play Store is installed.

In addition, for the wear-module, you must add In AndroidManifest.xml <uses-feature> with the value:

 <uses-feature android:name="" />


Previously, apk for Wear 1.0 were embedded phone APKs. Wear OS allows us separate apk compiled for Wear 1.0 and upload it directly into Play Store. This helps to decrease phone apk size and gives flexibility in app versioning and distribution. “When you upload a watch APK via the Play Console, you can update your Wear APK independently from the phone APK, and users receive updates via the watch Play Store”  more…

Our app will be independent apk, but this doesn’t mean that we cannot use existing code again. Just extract common logic into a library module, and import it into app and wear. Thus, in app and wear modules, only the UI part of the application remains basically.



To create beautiful responsive applications in wear os there are new UI-elements . For example, BoxInsetLayout – automatically adapting to round and rectangular screens layout.

BoxInsetLayout      BoxInsetLayout

To create lists for wearable devices there is a RecyclerView analog – WearableRecyclerView





We created wearable module in our project and adapted existing application for Wear OS. Remember that because of hardware limitations watches are not designed for complex applications. They are ideal for notifications, quick messages, or simple applications. Keep this in mind, and publish new applications or Wear OS versions of existing apps on Google Play!

That’s all for today!

A/B Testing with Firebase. Simple. Fast.

Sometimes we need to test a new feature in our mobile app. For example, if you want to see how new design will affect retention and DAU, whether users like it or not. AB Testing helps us here.
In a simplified form, it looks like this: on the backend side, administrator exposes which part of the users will see option A (new design), and which part is version B (old design). The client-side requests the server which option, A or B, it should show. And then a regular if- or switch- check starts the necessary code.
Fortunately for many mobile developers, the multifunctional service Firebase provides among other the possibility of AB-tests.
To create an experiment open Firebase console.
Open your project. If you do not have a project yet, create one.
For clarity, I will add an experiment to change the color of the UI element in my stopwatch project.
So, on the left, in the section Grow select A/B Testing and then Create experiment->Remote config. Enter the name of the experiment, description, select our application and enter the percentage of the target audience

Press Next. We may add not only two, but several options. In my case, two is enough. Now add parameter red_circle_enabled. Set values for created varinats:

It remains to choose the goals by which we will monitor the effectiveness of the tests:

Press Review and start experiment.

Now we need to implement A/B logic on client side.

In Codelabs there is a good tutorial how to make it on Android
Everything we need belongs to, so make sure you have added next dependency in your build.gradle:

 implementation ''

Next, initialise Remote Config instance where you will implement A/B test logic (e.g. MainActivity):

FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();FirebaseRemoteConfigSettings firebaseRemoteConfigSettings =
 new FirebaseRemoteConfigSettings.Builder()
// Initialise default data
Map<String, Object> defaultConfigMap = new HashMap<>();
defaultConfigMap.put("red_circle_enabled", false);

fetchConfig() requests config data from Firebase

public void fetchConfig() {
    long cacheExpiration = 3600;
    if (firebaseRemoteConfig.getInfo().getConfigSettings()
            .isDeveloperModeEnabled()) {
        cacheExpiration = 0;
            .addOnSuccessListener(aVoid -> {
            .addOnFailureListener(e -> {
                Log.w(TAG, "Error fetching config: " +

applyRedCircleTest() directly implements the logic depending on the received A/B test value

private void applyRedCircleTest() {
    Boolean redCircleEnabled = firebaseRemoteConfig.getBoolean(Constants.AB_TEST_RED_CIRCLE);

That’s all! We added the A/B test to the application in 15 minutes using Firebase!

RxJava. ConcatMap for dependent Observables

Today I’m gonna tell how concatMap helps to transform an existing sequential dependent synchronous chain of methods into a reactive and multithreaded one.
Suppose we get a boolean value from a presenter, and perform some UI event:

  boolean state = mPresenter.getBooleanState(context)
  if(state) {
    } else {

The class, that provides data, Presenter in our case, has a dependent method structure, the second depends on the result of the first:

public class MyPresenter<MyView> {


  private int getIntValue(Context context) {
      int retValue = someCalculationMethod(context);
      return retValue;
  public boolean getBooleanState(Context context) {
      int intValue = getIntValue(context);
      return performSomeCalculationWith(intValue);

When someCalculationMethod executes immediately, it might be run on the main thread. If it for example, server request, and we may expect some time delay, then it should be executed in a background thread.
With Rx we can easily achieve it, preliminary transformed methods getIntValue and getBooleanState into Observable:

  private Observable<Integer> getIntValue(Context context) {
      return Observable.fromCallable(() -> {
         int retValue = someCalculationMethod(context);
         return retValue;

  private Observable<Boolean> getBooleanState(Context context, Integer intValue) {
     return Observable.fromCallable(() -> {
        boolean retValue = performSomeCalculationWith(intValue);
        return retValue;

So, how do we get Observable from the getBooleanState without having getIntValue performed? All magic on merging these two Observables is made by concatMap operator:

  private Observable<Boolean> rxGetBooleanState(Context context) {
      Observable integerObservable = getIntValue(context)

      Observable retObservable = integerObservable
        .concatMap(intValue -> getBooleanState(context, intValue));
      return retObservable;

ConcatMap works similarly to flatMap. Important distinctive feature is it keeps elements order.

Let’s check docs:

Returns a new Observable that emits items resulting from applying a function that you supply to each item emitted by the source Observable, where that function returns an Observable, and then emitting the items that result from concatenating those resulting Observables.

And look at the concatMap operator scheme:


concatMap scheme

concatMap applies a function (Observable) that you supply to each item emitted by the original Observable, and then merges the results of that function applied to every item emitted by the original Observable, thus creating a new Observable saving original order from first Observable.

And now we’re only need to call our method from appropriate context:

Disposable disposable = mPresenter.rxGetBooleanState(this)
        .subscribe { state ->
            if (required) {
            } else {

Actually, the easiest way to execute dependant observables is to use .flatMap operator, as described here:

getIntValue(context).flatMap( intValue -> {
   return getBooleanState(context, intValue)

RxJava. share operator with RxBinding

Today I’ll tell you about a convenient reactive approach to handle EditText input. As an example, we will take a search string in a SearchView.

The task

Search usually takes a while. No matter if we requesting data from the server, or read the local database. User may input letters in the textfield much faster than the requests would work off. Therefore we need to settle some time threshold not making any requests more frequent than necessary. Besides, we may need to update some UI changes in real-time, without those time gaps.


Without Rx in Android we can use Runnable and Handler, which is not handy. But thanks to the RxBinding we may use the debounce operator and get an elegant solution:

 .debounce(500, TimeUnit.MILLISECONDS) // delay for 500 ms
 .subscribe(query -> mPresenter.searchRequest(query));

Now, regardless of input speed, requests to the server will be performed no more often than twice a second.
But, what should we do, if we need to update UI on every letter input? For example, hint needs to be changed. Using the code above we would have debounced UI updates with 500 ms, what is not quite user-friendly.

Fortunately, in Rx there is the possibility to broadcast to multiple subscribers. In order to use it we need to call .share() on our Observable and voila!

Observable<String> sharedTextChanges = RxSearchView.queryTextChages(searchViw).share()
 .debounce(500, TimeUnit.MILLISECONDS) // use debounce 
 .subscribe(query -> mPresenter.searchRequest(query)); 
 .subscribe(query -> mPresenter.updateUI(query));

We subscribed to Observable twice: with and without debounce. Now UI refreshes on every letter input, whereas server request being performed no more frequent than 500 ms.

What’s under the hood?

Operator .share() is an wrapper for .publish().refcount(). They allow to “share” items emitted by a stream. Let’s look deeper.

Operator.publish( ) — transforms Observable into ConnectableObservable.

rxjava publish operator scheme

“ConnectedObservable” is an Observable, which is not emitting data until someone wouldn’t call .connect() operator on it.

Operator .refcount() controls multiple subscribers. According to the docs,

Returns an Observable that stays connected to this ConnectableObservable as long as there is at least one subscription to this ConnectableObservable.

RxJava operator refcount

refcount() operator knows how many subscribers are subscribed to the Observable and doesn’t disconnect from the source ConnectedObservable until all Observables are unsubscribed.

That’s all. Very effective and elegant, as always with Rx. Hope this post was useful for you!