24 December 2018

Subscribing on Main Thread Might Be Delayed

In Android applications, some (many?) operations tend to be executed asynchronously. Consider the following code:

// in some activity

fun handleClick() {

    mObservable
      .subscribeOn(AndroidSchedulers.mainThread())
      .subscribe({
        toast("Operation finished")
      }, {
        Log.e("tag", it)
      })

    mManager.doAsyncWork()
}

The intent is to initiate some possibly long running work and later notify user that work is completed. doAsyncWork would (possibly indirectly) at some point emit a new item which signals completion on work.

However, there is a bug here.

If doAsyncWork is fast enough, new item will be emitted before subscription is actually connected to observable and so the subscriber code will never receive the item.

Calling subscribeOn(AndroidSchedulers.mainThread()) apparently changes the process of connecting even though it might seem nothing should happen as the code is already executing on the main thread.

07 October 2018

Poor Man's Profiler

If you’re having issues regarding performance, i.e. application is slow, a quick and dirty solution can be to

  1. trigger slow code (from UI, etc.) and then
  2. press pause button in the IDE Image of pause button in Android studio

Chances are that two out of three times call stack will be stuck in the expensive method call.

Image of call stack

Then, you can simply have a glance at what each method does:

fun innocentMethod1() {
    innocentMethod2()
}

fun innocentMethod2() {
    innocentLookingMethod()
}

Nothing suspicious here.

But, move on and you might see something like:

fun innocentLookingMethod() {
    for (i in 1..1000000) {
        innocentMethod3(i)
    }
}

which should be a good enough starting point for further work.

Credits

Credits for the idea, however it may seem simple, are due to a blog or forum post somewhere, sometime.

03 October 2018

Converting Weak Listener to Rx Observable

Andrea Maglie has a nice writeup on how to nicely convert classic observable with listeners to Rx observable. Here is the code snippet:

public Observable<String> observableListenerWrapper() {
    return Observable.create(new Observable.OnSubscribe<String>() {

        @Override
        public void call(Subscriber<? super String> subscriber) {
            ValueUpdateListener listener = new ValueUpdateListener() {

                @Override
                public void onValueChanged(@NonNull String value) {
                    if (subscriber.isUnsubscribed()) {
                        valueHolder.unregisterListener(this);
                    } else {
                        subscriber.onNext(value);
                    }
                }
            };

            valueHolder.registerListener(listener);
        }
    });
}

where ValueHolder holds a collection on weak references to listeners:

class ValueHolder {
  private final List<WeakReference<ValueUpdateListener>> fObservers = new CopyOnWriteArrayList<>();
  
  private void notifyListeners(String newValue) {
    for (WeakReference<ValueUpdateListener> ref : fObservers) {        
      final var listener = ref.get();
      if (listener != null ) {
        listener.onValueChanged(newValue);
      }
      else {
        fObservers.remove(ref)        
      }
    }
  }
 
  public void addListener(ValueUpdateListener listener) {
    fObservers.add(new WeakReference<ValueUpdateListener>(listener));
  }
}

After the above code is converted to Kotlin and RxJava 2 we have the following

object Consumer {
    private var mDisposable: Disposable? = null

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            valueHolder.registerListener(object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            })
        }
        .subscribe(
            { println(it) },
            { it.printStackTrace() }
        )
    }
}

which… does not work.

Apparently, the object implementing listener gets garbage collected. Consequently, when ValueHolder goes thru the list of listeners it notices that weak reference points to nothing and removes it from listener collection. First attempt to fix this was to extract listener object to a variable in hope it would be captured in emitter lambda.

object Consumer {
    private var mDisposable: Disposable? = null

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            val listener = object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            }

            valueHolder.registerListener(listener)
        }
        .subscribe(
            { println(it) },
            { it.printStackTrace() }
        )
    }
}

That did not help. Somehow, the lambda was not capturing the reference to listener. Moving listener object all the way out to singleton corrected the issue, as it survived garbage collection and Rx observable continued to work as expected.

object Consumer {
    private var mDisposable: Disposable? = null
    private lateinit var mListener: ValueUpdateListener

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            mListener = object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            }

            valueHolder.registerListener(mListener)
        }
        .subscribe(
             { println(it) },
             { it.printStackTrace() }
        )
    }
}

Conclusion

Be cautious when mixing lambdas, Rx and weak references, as the end result can be unexpected.

Updated on 2018-12-23

Added implementation of ValueHolder.