In this article we are going to learn useful operators in RxJava i.e Buffer and Debounce. Although these operators sounds same, the purpose of these two is completely different. In this, simple android use cases are conidered to explain the operators better and make them easier to understand.

If you are new to RxJava operators, Operators Introduction is a good place to get started.

1. Buffer

Buffer gathers items emitted by an Observable into batches and emit the batch instead of emitting one item at a time.

Below, we have an Observable that emits integers from 1-9. When buffer(3) is used, it emits 3 integers at a time.

        Observable<Integer> integerObservable = Observable.just(1, 2, 3, 4,
                5, 6, 7, 8, 9);

        integerObservable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .buffer(3)
                .subscribe(new Observer<List<Integer>>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(List<Integer> integers) {
                        Log.d(TAG, "onNext");
                        for (Integer integer : integers) {
                            Log.d(TAG, "Item: " + integer);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "All items emitted!");
                    }
                });

If you run the example, you can see onNext() received List of integers on every emission.

Output
onNext
Item: 1
Item: 2
Item: 3
onNext
Item: 4
Item: 5
Item: 6
onNext
Item: 7
Item: 8
Item: 9
All items emitted!

Now let’s consider a more useful android scenario. Let’s say you want to track number of taps performed in a specified time period, using buffer it can be done very easily.

Below, we have a Button and two TextViews. The tap will be performed on the Button and the TextViews are to display the result (current taps and maximum taps performed).

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:orientation="vertical"
    tools:context="info.androidhive.rxandroidexamples.operators.BufferOperatorActivity">

    <Button
        android:id="@+id/layout_tap_area"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_margin="@dimen/activity_margin"
        android:clickable="true"
        android:gravity="center"
        android:text="Tap repeatedly here!"
        android:textAllCaps="false" />

    <TextView
        android:id="@+id/tap_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal" />

    <TextView
        android:id="@+id/tap_result_max_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal" />


</LinearLayout>
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;

import com.jakewharton.rxbinding2.view.RxView;

import java.util.List;
import java.util.concurrent.TimeUnit;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import info.androidhive.rxandroidexamples.R;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;

public class BufferOperatorActivity extends AppCompatActivity {
    private static final String TAG = BufferOperatorActivity.class.getSimpleName();

    @BindView(R.id.tap_result)
    TextView txtTapResult;

    @BindView(R.id.tap_result_max_count)
    TextView txtTapResultMax;

    @BindView(R.id.layout_tap_area)
    Button btnTapArea;

    private Disposable disposable;
    private Unbinder unbinder;
    private int maxTaps = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_buffer_operator);
        unbinder = ButterKnife.bind(this);

        RxView.clicks(btnTapArea)
                .map(new Function<Object, Integer>() {
                    @Override
                    public Integer apply(Object o) throws Exception {
                        return 1;
                    }
                })
                .buffer(3, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new Observer<List<Integer>>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                    }

                    @Override
                    public void onNext(List<Integer> integers) {
                        Log.e(TAG, "onNext: " + integers.size() + " taps received!");
                        if (integers.size() > 0) {
                            maxTaps = integers.size() > maxTaps ? integers.size() : maxTaps;
                            txtTapResult.setText(String.format("Received %d taps in 3 secs", integers.size()));
                            txtTapResultMax.setText(String.format("Maximum of %d taps received in this session", maxTaps));
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "onError: " + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "onComplete");
                    }
                });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
        disposable.dispose();
    }
}
rxjava-buffer-operator-example

2. Debounce

Debounce operators emits items only when a specified timespan is passed. This operator is very useful when the Observable is rapidly emitting items but you are only interested in receiving them in timely manner.

Consider an example of performing an instant search. When user types the search query, the query will be sent to server and the result will be displayed. The request will be sent to server every time user types a character which makes unnecessary calls to server with incomplete search word. Instead, we can wait for a certain time period until user types proper search keyword then we can send the request to server.

  • Let’s say user want to search for `RxJava`. Without debounce, there would be multiple calls to server for keywords `R`, `Rx`, `RxJ` and so on. Instead we can give user a time period say 300 milli sec to type and send the query the to server. Most probably user can type upto `RxJ` in the given time period.
  • Here, we have used debounce(300, TimeUnit.MILLISECONDS) which means the query will be emitted every 300 milli seconds
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.EditText;
import android.widget.TextView;

import com.jakewharton.rxbinding2.widget.RxTextView;
import com.jakewharton.rxbinding2.widget.TextViewTextChangeEvent;

import java.util.concurrent.TimeUnit;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import info.androidhive.rxandroidexamples.R;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;

public class DebounceOperatorActivity extends AppCompatActivity {

    private static final String TAG = DebounceOperatorActivity.class.getSimpleName();

    private CompositeDisposable disposable = new CompositeDisposable();
    private Unbinder unbinder;

    @BindView(R.id.input_search)
    EditText inputSearch;

    @BindView(R.id.txt_search_string)
    TextView txtSearchString;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_debounce_operator);
        unbinder = ButterKnife.bind(this);

        disposable.add(
                RxTextView.textChangeEvents(inputSearch)
                        .skipInitialValue()
                        .debounce(300, TimeUnit.MILLISECONDS)
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeWith(searchQuery()));

        txtSearchString.setText("Search query will be accumulated every 300 milli sec");
    }

    private DisposableObserver<TextViewTextChangeEvent> searchQuery() {
        return new DisposableObserver<TextViewTextChangeEvent>() {
            @Override
            public void onNext(TextViewTextChangeEvent textViewTextChangeEvent) {
                Log.d(TAG, "search string: " + textViewTextChangeEvent.text().toString());

                txtSearchString.setText("Query: " + textViewTextChangeEvent.text().toString());
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        };
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
        disposable.clear();
    }
}
rxjava-debounce-operator-search-example

Android Instant Search with Remote Database

The above example display the only the search query just to demonstrate the use of Debounce. If you are looking for full implementation with a database, read Instant Search with a Remote Database article.

I hope this articles clearly explained the use of Buffer and Debounce. If you want to explore more RxJava operator, read the list here.

Author

  • Onuwa Nnachi Isaac

    Wonderful… Started following your site today… Kudos for your wonderful tutorials

  • Suresh Bora

    @Jeet Singh Nice Trick Thanks

  • Pragnesh

    Great man!

  • Kuls

    Thanks buddy

  • Nguyễn Đức Quang

    Thanks so much.:))

  • Feni Kadivar

    thank u soo much…its working …!!

  • Sandeep Autade

    Thanks you

  • cindy

    not working.

  • Santhosh Sapi

    Thanks a lot for the article. But i am facing some issue. Yesterday i started to port all my projects to Android Studio. I tried everything still GoogleCloudMessaging is having an error as ‘Cannot resolve symbol’.

    This is what i have.
    dependencies {
    compile ‘com.android.support:appcompat-v7:23.2.0’
    compile ‘com.google.android.gms:play-services:8.4.0’
    }

    Please help me in fixing this.

  • venkat

    Is it possible to achieve multiple messages in single notification like whats App and Gmail?

  • Kb

    I am trying to set BigContentTitle but the notification still won’t display the entire title

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
    .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
    .setSmallIcon(getNotificationIcon())
    .setContentTitle(title)
    .setContentText(messageBody);

    NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle(notificationBuilder);
    style.bigText(messageBody);
    style.setBigContentTitle(title);

    what am I doing wrong ?

  • HimanAshu Srivastava

    This article is really very helpful. But I want to change notification text color, how can I do that?

  • sagu hildon

    HI Have you tried in C# ?
    Can you please tell how to send multiple message to same device id (GCM)?
    Below is the postdata to send message using GCM service,
    string postData = “collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.message=”
    + value + “&data.time=” + System.DateTime.Now.ToString() + “&data.title=”
    + “MCT Alert Notification” + “&registration_id=” + regId + “”;

  • Saurabh Gaddelpalliwar

    I am trying to set Bigtext but the notification still single line message ?

    mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
    mBuilder.setContentText(message);

    i want to show multiple line in notification, I am using Firebase Push Notification.

    • Which device you are testing it on? Some devices (like MI) won’t show big text notifications.

  • Chandan Kumar

    Hi Ravi, While using NotificationCompat.BigPictureStyle I want to put summary in two lines. How I can achieve it ?

  • Archi rayan

    Hello ,

    How to make Expand Notification same like whatsapp. fullscreen view the Notification

    please, any one idea?

  • Pankaj Negi

    Hello Ravi. I find this article very helpful to learn RxJava. Thanks to provide these tutorials.
    I am facing issue in this article related to RxView.clicks(btnTapArea) in Buffer Operator Example ?? What is RxView ?? I can’t able to find definition for this.

  • Keerthi Prasad

    I wanted to implement the same button click listener without using RxBinding library.can u please help me with the example.

  • Khushnidjon Keldiboev

    Really cool man, for a long time i searched such blog

  • Ashik Azeez

    Nice tutorial and very useful