MCPcopy
hub / github.com/kaushikgopal/RxJava-Android-Samples

github.com/kaushikgopal/RxJava-Android-Samples @main sqlite

repository ↗ · DeepWiki ↗
361 symbols 967 edges 40 files 21 documented · 6%
README

Learning RxJava for Android by example

This is a repository with real-world useful examples of using RxJava with Android. It usually will be in a constant state of "Work in Progress" (WIP).

I've also been giving talks about Learning Rx using many of the examples listed in this repo.

Examples:

  1. Background work & concurrency (using Schedulers)
  2. Accumulate calls (using buffer)
  3. Instant/Auto searching text listeners (using Subjects & debounce)
  4. Networking with Retrofit & RxJava (using zip, flatmap)
  5. Two-way data binding for TextViews (using PublishSubject)
  6. Simple and Advanced polling (using interval and repeatWhen)
  7. Simple and Advanced exponential backoff (using delay and retryWhen)
  8. Form validation (using combineLatest)
  9. Pseudo caching : retrieve data first from a cache, then a network call (using concat, concatEager, merge or publish)
  10. Simple timing demos (using timer, interval or delay)
  11. RxBus : event bus using RxJava (using RxRelay (never terminating Subjects) and debouncedBuffer)
  12. Persist data on Activity rotations (using Subjects and retained Fragments)
  13. Networking with Volley
  14. Pagination with Rx (using Subjects)
  15. Orchestrating Observable: make parallel network calls, then combine the result into a single data point (using flatmap & zip)
  16. Simple Timeout example (using timeout)
  17. Setup and teardown resources (using using)
  18. Multicast playground

Description

1. Background work & concurrency (using Schedulers)

A common requirement is to offload lengthy heavy I/O intensive operations to a background thread (non-UI thread) and feed the results back to the UI/main thread, on completion. This is a demo of how long-running operations can be offloaded to a background thread. After the operation is done, we resume back on the main thread. All using RxJava! Think of this as a replacement to AsyncTasks.

The long operation is simulated by a blocking Thread.sleep call (since this is done in a background thread, our UI is never interrupted).

To really see this example shine. Hit the button multiple times and see how the button click (which is a UI operation) is never blocked because the long operation only runs in the background.

2. Accumulate calls (using buffer)

This is a demo of how events can be accumulated using the "buffer" operation.

A button is provided and we accumulate the number of clicks on that button, over a span of time and then spit out the final results.

If you hit the button once, you'll get a message saying the button was hit once. If you hit it 5 times continuously within a span of 2 seconds, then you get a single log, saying you hit that button 5 times (vs 5 individual logs saying "Button hit once").

Note:

If you're looking for a more foolproof solution that accumulates "continuous" taps vs just the number of taps within a time span, look at the EventBus Demo where a combo of the publish and buffer operators is used. For a more detailed explanation, you can also have a look at this blog post.

3. Instant/Auto searching text listeners (using Subjects & debounce)

This is a demo of how events can be swallowed in a way that only the last one is respected. A typical example of this is instant search result boxes. As you type the word "Bruce Lee", you don't want to execute searches for B, Br, Bru, Bruce, Bruce, Bruce L ... etc. But rather intelligently wait for a couple of moments, make sure the user has finished typing the whole word, and then shoot out a single call for "Bruce Lee".

As you type in the input box, it will not shoot out log messages at every single input character change, but rather only pick the lastly emitted event (i.e. input) and log that.

This is the debounce/throttleWithTimeout method in RxJava.

4. Networking with Retrofit & RxJava (using zip, flatmap)

Retrofit from Square is an amazing library that helps with easy networking (even if you haven't made the jump to RxJava just yet, you really should check it out). It works even better with RxJava and these are examples hitting the GitHub API, taken straight up from the android demigod-developer Jake Wharton's talk at Netflix. You can watch the talk at this link. Incidentally, my motivation to use RxJava was from attending this talk at Netflix.

(Note: you're most likely to hit the GitHub API quota pretty fast so send in an OAuth-token as a parameter if you want to keep running these examples often).

5. Two-way data binding for TextViews (using PublishSubject)

Auto-updating views are a pretty cool thing. If you've dealt with Angular JS before, they have a pretty nifty concept called "two-way data binding", so when an HTML element is bound to a model/entity object, it constantly "listens" to changes on that entity and auto-updates its state based on the model. Using the technique in this example, you could potentially use a pattern like the Presentation View Model pattern with great ease.

While the example here is pretty rudimentary, the technique used to achieve the double binding using a Publish Subject is much more interesting.

6. Simple and Advanced polling (using interval and repeatWhen)

This is an example of polling using RxJava Schedulers. This is useful in cases, where you want to constantly poll a server and possibly get new data. The network call is "simulated" so it forces a delay before return a resultant string.

There are two variants for this:

  1. Simple Polling: say when you want to execute a certain task every 5 seconds
  2. Increasing Delayed Polling: say when you want to execute a task first in 1 second, then in 2 seconds, then 3 and so on.

The second example is basically a variant of Exponential Backoff.

Instead of using a RetryWithDelay, we use a RepeatWithDelay here. To understand the difference between Retry(When) and Repeat(When) I wouuld suggest Dan's fantastic post on the subject.

An alternative approach to delayed polling without the use of repeatWhen would be using chained nested delay observables. See startExecutingWithExponentialBackoffDelay in the ExponentialBackOffFragment example.

7. Simple and Advanced exponential backoff (using delay and retryWhen)

Exponential backoff is a strategy where based on feedback from a certain output, we alter the rate of a process (usually reducing the number of retries or increasing the wait time before retrying or re-executing a certain process).

The concept makes more sense with examples. RxJava makes it (relatively) simple to implement such a strategy. My thanks to Mike for suggesting the idea.

Retry (if error) with exponential backoff

Say you have a network failure. A sensible strategy would be to NOT keep retrying your network call every 1 second. It would be smart instead (nay... elegant!) to retry with increasing delays. So you try at second 1 to execute the network call, no dice? try after 10 seconds... negatory? try after 20 seconds, no cookie? try after 1 minute. If this thing is still failing, you got to give up on the network yo!

We simulate this behaviour using RxJava with the retryWhen operator.

RetryWithDelay code snippet courtesy:

  • http://stackoverflow.com/a/25292833/159825
  • Another excellent implementation via @sddamico : https://gist.github.com/sddamico/c45d7cdabc41e663bea1
  • This one includes support for jittering, by @leandrofavarin : http://leandrofavarin.com/exponential-backoff-rxjava-operator-with-jitter

Also look at the Polling example where we use a very similar Exponential backoff mechanism.

"Repeat" with exponential backoff

Another variant of the exponential backoff strategy is to execute an operation for a given number of times but with delayed intervals. So you execute a certain operation 1 second from now, then you execute it again 10 seconds from now, then you execute the operation 20 seconds from now. After a grand total of 3 times you stop executing.

Simulating this behavior is actually way more simpler than the prevoius retry mechanism. You can use a variant of the delay operator to achieve this.

8. Form validation (using .combineLatest)

Thanks to Dan Lew for giving me this idea in the fragmented podcast - episode #4 (around the 4:30 mark).

.combineLatest allows you to monitor the state of multiple observables at once compactly at a single location. The example demonstrated shows how you can use .combineLatest to validate a basic form. There are 3 primary inputs for this form to be considered "valid" (an email, a password and a number). The form will turn valid (the text below turns blue :P) once all the inputs are valid. If they are not, an error is shown against the invalid inputs.

We have 3 independent observables that track the text/input changes for each of the form fields (RxAndroid's WidgetObservable comes in handy to monitor the text changes). After an event change is noticed from all 3 inputs, the result is "combined" and the form is evaluated for validity.

Note that the Func3 function that checks for validity, kicks in only after ALL 3 inputs have received a text change event.

The value of this technique becomes more apparent when you have more number of input fields in a form. Handling it otherwise with a bunch of booleans makes the code cluttered and kind of difficult to follow. But using .combineLatest all that logic is concentrated in a nice compact block of code (I still use booleans but that was to make the example more readable).

9. Pseudo caching : retrieve data first from a cache, then a network call (using concat, concatEager, merge or publish)

We have two source Observables: a disk (fast) cache and a network (fresh) call. Typically the disk Observable is much faster than the network Observable. But in order to demonstrate the working, we've also used a fake "slower" disk cache just to see how the operators behave.

This is demonstrated using 4 techniques:

  1. .concat
  2. .concatEager
  3. .merge
  4. .publish selector + merge + takeUntil

The 4th technique is probably what you want to use eventually but it's interesting to go through the progression of techniques, to understand why.

concat is great. It retrieves information from the first Observable (disk cache in our case) and then the subsequent network Observable. Since the disk cache is presumably faster, all appears well and the disk cache is loaded up fast, and once the network call finishes we swap out the "fresh" results.

The problem with concat is that the subsequent observable doesn't even start until the first Observable completes. That can be a problem. We want all observables to start simultaneously but produce the results in a way we expect. Thankfully RxJava introduced concatEager which does exactly that. It starts both observables but buffers the result from the latter one until the former Observable finishes. This is a completely viable option.

Sometimes though, you just want to start showing the results immediately. Assuming the first observable (for some strange reason) takes really long to run through all its i

Extension points exported contracts — how you extend this code

GithubApi (Interface)
(no doc)
app/src/main/java/com/morihacky/android/rxjava/retrofit/GithubApi.java
IAmYourMaster (Interface)
(no doc)
app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist1WorkerFragment.java
IAmYourMaster (Interface)
(no doc)
app/src/main/java/com/morihacky/android/rxjava/fragments/RotationPersist2WorkerFragment.java

Core symbols most depended-on inside this repo

subscribe
called by 36
app/src/main/java/com/morihacky/android/rxjava/fragments/TimeoutDemoFragment.java
_log
called by 19
app/src/main/java/com/morihacky/android/rxjava/fragments/TimingDemoFragment.java
_getCurrentTimestamp
called by 19
app/src/main/java/com/morihacky/android/rxjava/fragments/TimingDemoFragment.java
clickedOn
called by 18
app/src/main/java/com/morihacky/android/rxjava/fragments/MainFragment.java
create
called by 14
app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationAdapter.java
get
called by 9
app/src/main/java/com/morihacky/android/rxjava/MyApp.java
_log
called by 7
app/src/main/java/com/morihacky/android/rxjava/fragments/ExponentialBackoffFragment.java
getRxBusSingleton
called by 6
app/src/main/java/com/morihacky/android/rxjava/MainActivity.java

Shape

Method 306
Class 52
Interface 3

Languages

Java100%

Modules by API surface

app/src/main/java/com/morihacky/android/rxjava/fragments/MainFragment.java22 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/PseudoCacheFragment.java18 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/TimeoutDemoFragment.java17 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/PollingFragment.java17 symbols
app/src/main/java/com/morihacky/android/rxjava/pagination/PaginationAdapter.java16 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/TimingDemoFragment.java16 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/ExponentialBackoffFragment.java16 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/ConcurrencyWithSchedulersDemoFragment.java16 symbols
app/src/main/java/com/morihacky/android/rxjava/volley/VolleyDemoFragment.java15 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/NetworkDetectorFragment.java14 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/DebounceSearchEmitterFragment.java14 symbols
app/src/main/java/com/morihacky/android/rxjava/fragments/BufferDemoFragment.java13 symbols

For agents

$ claude mcp add RxJava-Android-Samples \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact