Chapters

Hide chapters

Reactive Programming with Kotlin

Second Edition · Android 10 · Kotlin 1.3 · Android Studio 4.0

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section II: Operators & Best Practices

Section 2: 7 chapters
Show chapters Hide chapters

6. Filtering Operators in Practice
Written by Alex Sullivan & Marin Todorov

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

In the previous chapter, you began your introduction to the functional aspect of RxJava. The first batch of operators you learned about helped you filter the elements of an observable sequence. As explained previously, the operators are simply methods on Observable and other associated RxJava types.

The operators operate on the elements of their Observable class and produce a new observable sequence as a result. This comes in handy because, as you saw previously, this allows you to chain operators, one after another, and perform several transformations in sequence:

The preceding diagram looks great in theory. In this chapter, you’re going to try using the filtering operators in a real-life app. In fact, you are going to continue working on the Combinestagram app that you already know and love from Chapter 4, “Observables & Subjects in Practice”.

Note: In this chapter, you will need to understand the theory behind the filtering operators in RxJava. If you haven’t worked through Chapter 5, “Filtering Operators,” do that first and then come back to the current chapter.

Improving the Combinestagram project

In this chapter, you will:

  • Work through series of tasks, which (surprise!) will require you to use various filtering operators.
  • Use different ones and see how you can use counterparts like skip and take.
  • Take care of a few of the issues in the current Combinestagram project.

Note: Since this book has only covered a few operators so far, you will not write the “best possible” code. For this chapter, don’t worry about best practices or proper architecture yet, but instead focus on truly understanding how to use the filtering operators. In this book, you’re going to slowly build up towards writing good RxJava code. It’s a process!

Refining the photos sequence

Currently, the main screen of the app looks like this:

Sharing subscriptions

Is there anything wrong with calling subscribe(...) on the same observable multiple times? Turns out there might be!

val numbers = Observable.create<Int> { emitter ->
  val start = getStartNumber()
  emitter.onNext(start)
  emitter.onNext(start + 1)
  emitter.onNext(start + 2)
  emitter.onComplete()
}
var start = 0
private fun getStartNumber(): Int {
  start++
  return start
}
numbers
  .subscribeBy(
      onNext = { println("element [$it]") },
      onComplete = { println(("-------------"))}
  ))
element [1]
element [2]
element [3]
-------------
element [1]
element [2]
element [3]
-------------
element [2]
element [3]
element [4]
-------------
val newPhotos = fragment.selectedPhotos.share()
subscriptions.add(newPhotos
    .doOnComplete {
      Log.v("SharedViewModel", "Completed selecting photos")
    }
    .subscribe { photo ->
      imagesSubject.value?.add(photo)
      imagesSubject.onNext(imagesSubject.value ?:
        mutableListOf())
    }
)

Ignoring all elements

You will start with the simplest filtering operator: the one that filters out all elements. No matter your value or type, ignoreElements() says “You shall not pass!”

subscriptions.add(newPhotos
    .ignoreElements()
    .subscribe {
      
    })
enum class ThumbnailStatus {
  READY,
  ERROR
}
private val thumbnailStatus = MutableLiveData<ThumbnailStatus>()
fun getThumbnailStatus(): LiveData<ThumbnailStatus> {
    return thumbnailStatus
}
subscriptions.add(newPhotos
  .ignoreElements()
  .subscribe {
    thumbnailStatus.postValue(ThumbnailStatus.READY)
}
viewModel.getThumbnailStatus().observe(this,
  Observer { status ->
    if (status == ThumbnailStatus.READY) {
      thumbnail.setImageDrawable(collageImage.drawable)
    }
  }
)

Filtering elements you don’t need

Of course, as great as ignoreElements() is, sometimes you will need to ignore just some of the elements — not all of them.

subscriptions.add(newPhotos
  .doOnComplete {
    // ..
  }
  .filter { newImage ->
    val bitmap = BitmapFactory.decodeResource(
    fragment.resources, newImage.drawable)
    bitmap.width > bitmap.height
  }
  .subscribe { photo ->
    // ..
  }
)

Implementing a basic uniqueness filter

Combinestagram, in its current form, has another controversial “feature”: you can add the same photo more than once. That doesn’t make for very interesting collages, so in this section you’ll add some advanced filtering to prevent the user from adding the same photo multiple times.

subscriptions.add(newPhotos
  .doOnComplete {
    // ..
  }
  .filter { newImage ->
    // ..
  // 1
  }
  .filter { newImage ->
    // 2
    val photos = imagesSubject.value ?: mutableListOf()
    // 3
    !(photos.map { it.drawable }
      // 4
      .contains(newImage.drawable))
  }
  .subscribe { photo ->
   // ..
  }
)

Keep taking elements while a condition is met

One of the “best” bugs in Combinestagram is that the Add button is disabled if you add six photos, which prevents you from adding any more images. But if you are in the photos bottom dialog fragment, you can add as many as you wish. There ought to be a way to limit those, right?

subscriptions.add(newPhotos
  .doOnComplete {
    // ..
  }
  .takeWhile {
    imagesSubject.value?.size ?: 0 < 6
  }
  .filter { newImage ->
    // ..
  }
  .filter { newImage ->
    // ..
  }
  .subscribe { photo ->
   // ..
  }
)

Improving the photo selector

One common source of bugs in Android applications is what happens when a user quickly taps on a button multiple times. My guess is you’ve been in an app before where you quickly tapped a button and saw the application display multiple new activities.

.debounce(250, TimeUnit.MILLISECONDS,
    AndroidSchedulers.mainThread())

Challenge

Challenge: Combinestagram’s source code

Your challenge is to notify the user that they’ve reached the photo limit once they add 6 photos. Here’s a few hints on how to proceed with this challenge.

Key points

  • You can share subscriptions to a single observable using share().
  • ignoreElements comes in handy when you want to only look for stop events.
  • Filtering out elements in an observable using filter lets you prevent certain elements from coming through the stream, like allowing only landscape and not portrait photos.
  • Implementing a uniqueness filter can be achieved by combining filter with the current value of a BehaviorSubject.
  • Debouncing with the debounce operator helps you to get around pesky bugs that occur in apps due to rapid user interactions with the interface.

Where to go from here?

You now have a handle on the first type of RxJava operators we’ll examine, filtering operators, and have used them in an Android app.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now