RxKotlin Multiple Observations

If you’ve been doing android development with RxJava or RxKotlin you know that it’s a steep learning curve. RxJava is a great skill that every android developer should have in their toolbox. It makes writing android apps much easier. I’m constantly learning new things with RxJava, and applying what I learn to my android applications. For example this last week I was looking to do some work on the UI thread at the start of a subscription. I thought I had lucked into an easy solution with doOnSubscribe, which from the docs is supposed to do just that:

Modifies the source Observable so that it invokes the given action when it is subscribed from its subscribers. Each subscription will result in an invocation of the given action except when the source Observable is reference counted, in which case the source Observable will invoke the given action for the first subscription.

My code looked something like the following:

Observable
       .just(1)
       .doOnSubscribe {
           toast("Subscription, Started")
       }
       .subscribeOn(Schedulers.io())
       .subscribe {
       }

Unfortunately when I went to use the operator I got a crash with the following exception

Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

However a little more experimenting and I found this actually worked:

Observable
       .just(1)
       .subscribeOn(Schedulers.io())
       .doOnSubscribe {
           toast("Subscription, Started")
       }
       .subscribe {
       }

Observation 1

We can have doOnSubscribe work on a different thread. Which is super useful as we can now display a ui message from doOnSubscribe and then do work in the background.

At first the working code didn’t make much sense, how was the doOnsubscribe method being called on the main thread? My hunch was that the it was simply running on the thread that the observable was created, e.g. the immediate thread. So I decided to wrap it and test that assumption:

Observable.just(1).observeOn(Schedulers.io()).subscribe {
   Observable
           .just(1)
           .subscribeOn(Schedulers.io())
           .doOnSubscribe {
               toast("Subscription, Started")
           }

           .subscribe {

           }
}

Sure enough this failed as the toast was no longer implicitly running on the Main Thread. In android studio I was seeing the following error again:

Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

Observation 2

We should be explicit about where we want things to run because implicit code isn’t as obvious and could lead to bugs later. Specifically this function behaves differently depending on which thread doWork is called from:

fun doWork(){
Observable
       .just(1)
       .subscribeOn(Schedulers.io())
       .doOnSubscribe {
           toast("Subscription, Started")
       }
       .subscribe {

       }
}

However, this function behaves the same regardless of which thread doWork() is called from:

fun doWork(){
Observable
       .just(1)
       .subscribeOn(Schedulers.io())
       .doOnSubscribe {
           toast("Subscription, Started")
       }
       .subscribeOn(AndroidSchedulers.mainThread())
       .subscribe {

       }
}

I didn’t believe this was correct so I redid the test in a slightly different format to reverify:

Observable
       .just(1)
       .map {
           Log.i("Log", "2) showing progress on:" + Thread.currentThread().name)
           it
       }
       .subscribeOn(Schedulers.io())
       .doOnSubscribe {
           Log.i("Log", "1) doOnSubscribe with thread:" + Thread.currentThread().name)
       }
       .subscribeOn(AndroidSchedulers.mainThread())
       .subscribe {

       }

This yielded the following output:

I/Log: 1) doOnSubscribe with thread:main
I/Log: 2) showing progress on:RxCachedThreadScheduler-1

Observation 3

We can have multiple doOnSubscribes, and have them run on different schedulers. Note it is worth pointing out that subscribeOn can’t be used be used multiple times for all operations. For example:

Observable
       .just(1)
       .map {
           Log.i("Log", "2) showing progress on:" + Thread.currentThread().name)
           it
       }
       .subscribeOn(Schedulers.io())
       .map{
           Log.i("Log", "1) doOnSubscribe with thread:" + Thread.currentThread().name)
       }
       .subscribeOn(AndroidSchedulers.mainThread())
       .subscribe {

       }

Doesn’t cause work to be done on different threads. It simply uses the first doOnSubscribe. It generates the following output:

I/Log: 2) showing progress on:RxCachedThreadScheduler-1
I/Log: 1) doOnSubscribe with thread:RxCachedThreadScheduler-1

Often times we have to do some processing in the background and it would be nice to occasionally update the UI say between operations in our chain:

Observable
       .just(1)
       .map {
           Log.i("Log","1) doing work on:" + Thread.currentThread().name)
       }
       .subscribeOn(Schedulers.io())
       .observeOn(AndroidSchedulers.mainThread())
       .map {
           Log.i("Log","2) showing progress on:" + Thread.currentThread().name)
       }
       .observeOn(Schedulers.io())
       .map {
           Log.i("Log","3) more work on:" + Thread.currentThread().name)
       }
       .observeOn(AndroidSchedulers.mainThread())
       .subscribe {

       }

Which yielded the following in the log:

Log: 1) doing work on:RxCachedThreadScheduler-2
Log: 2) showing progress on:main
Log: 3) more work on:RxCachedThreadScheduler-1

Observation 5

We can use ObserveOn multiple times to interleave UI updates.

Revisiting the documentation:

Observable will use below where that operator appears. For this reason, you may call ObserveOn multiple times at various points during the chain of Observable operators in order to change on which threads certain of those operators operate.

These are some useful observations I noticed while trying to tackle this problem, proving again that Rx is very concise.

Read More

Smaller APKs with Classy Shark

A big thanks to my friend Enrique López Mañas for giving me feedback on how to improve this post, and of course Boris Farber for creating Classy Shark.

App Size

An app I am working on feels a lot larger than it should be, my hunch that this is due to dependencies that are being included in the APK that aren’t needed. Using Classy Shark I was able to start reducing the APK size.

Sometimes we need to use a library that depends on other libraries:
(more…)

Read More

Exploring Tiles in Android N Preview Release

Some amazing cakeThere are some cool new features slated for N(I’m voting that N stands for Nutella, cause this cake is amazing).

In this post I will be covering my discoveries while using the new Tiles API.

Note the online android developer docs don’t yet contain info about the new N APIs, but you can download the docs here for offline reading. For directions about getting setup to use the preview directions are here.

(more…)

Read More

Go With the Flow – Handling Configuration Changes with Flow

Last week we covered how to use flow with a master-detail view. I got some great feedback from Ray Ryan and Logan Johnson from the Square team. This week I’m going to be covering how to handle rotation.

Crash on Rotation

Screen Shot 2016-03-05 at 2.22.14 PM

If you had tried rotating last weeks example you would have noticed a bug. Specifically when you rotate from the master-detail in landscape to portrait that it would crash. We’re going to figure out how to solve that. First let’s take a look at our stack trace:



(more…)

Read More

Code Highlighting with Prism.JS

I have been using prismjs for a bit now. I like it. I noticed today though that it doesn’t color annotations in Java. My initial attempt at annotation coloring worked.

Prism.languages.java = Prism.languages.extend('clike', {
        'annotation': /(@{1}\w*\(.*?\))|(@{1}\w+)/i,
        'keyword': /\b(abstract|continue|for|new|switch|assert|default|goto...)\b/,
        'number': /\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,
        'operator': {
                pattern: /(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<<?=?|>>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,
                lookbehind: true
        }
});

Until but it failed to color annotations with parameters for some reason. A bit of debugging and I discovered that the function attribute was matching before the annotation attribute had a chance to run. At first I tried overriding the function attribute. That was potentially very damaging and made me super nervous. After a lot of hunting I discovered the insertBefore. This allows us to specify order. The first parameter is the language, the second is the attribute that we want to go before, and the following are the attributes we want to define. In this case we are defining annotation to happen before function. This works!

Prism.languages.java = Prism.languages.extend('clike', {
        'keyword': /\b(abstract|continue|for|new|switch|assert|default|goto...)\b/,
        'number': /\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,
        'operator': {
                pattern: /(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<<?=?|>>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,
                lookbehind: true
        }
});

Prism.languages.insertBefore('java','function', {
        'annotation': /(@{1}\w*\(.*?\))|(@{1}\w+)/i,
});

You can see my commit here

Read More

Master Detail with Square’s Flow

Update

I got some amazing feedback from Ray Rayan and Logan Johnson. I’ll likely be creating a supplemental post to cover their notes. One thing that should definitely go in this post is that Flow is in Alpha and the API isn’t stable. The api can change, so if you decide to use it and I hope you do 😉 you’ve been warned.

There is a lot of debate about if Fragments are good or bad. I think it’s good to explore both sides of the argument. In this post I will be covering my initial attempt at creating a master detail sample app that works both on the smaller phone size screen and tablet using Square’s Flow library.

Understanding of Flow

Name UI states, navigate between them, remember where you’ve been.

(more…)

Read More