10

The library I'm using emits a series of Message objects using callback object.

interface MessageCallback {
    onMessage(Message message);
}

The callback is added using some libraryObject.setCallback(MessageCallback) call and the process is started using non-blocking libraryObject.start() method call.

What is the best way of creating an Observable<Message> that will emit those objects?

What if the libraryObject.start() is blocking?

atok
  • 5,880
  • 3
  • 33
  • 62

2 Answers2

6

1. Callback invoked infinite number of times

We can convert it to Observable like this (example for RxJava 2):

Observable<Message> source = Observable.create(emitter -> {
        MessageCallback callback = message -> emitter.onNext(message);
        libraryObject.setCallback(callback);
        Schedulers.io().scheduleDirect(libraryObject::start);
        emitter.setCancellable(() -> libraryObject.removeCallback(callback));
    })
    .share(); // make it hot

share makes this observable hot, i.e. multiple subscribers will share single subscription, i.e. there will be at most one callback registered with libraryObject.

I used io scheduler to schedule start call to be made from background thread, so it does not delay first subscription.

2. Single message callback

It is quite common scenario as well. Let's say we have the following callback-style asynchronous method:

libraryObject.requestDataAsync(Some parameters, MessageCallback callback);

Then we can convert it to Observable like this (example for RxJava 2):

Observable<Message> makeRequest(parameters) {
    return Observable.create(emitter -> {
        libraryObject.requestDataAsync(parameters, message -> {
            emitter.onNext(message);
            emitter.onComplete();
        });
    });
}
Yaroslav Stavnichiy
  • 20,738
  • 6
  • 52
  • 55
  • I'm trying to get my head around RxJava, coming from RxJS. This is getting towards something I would like to know. How can you wrap an observable round an API which requires a value to be returned to it? In RxJS, you would use fromEventPattern and a wrapper function, I can't find the equivalent in RxJava, – Tom Jan 09 '19 at 02:52
2

I think you need something like this (example given in scala)

import rx.lang.scala.{Observable, Subscriber}

case class Message(message: String)

trait MessageCallback {
  def onMessage(message: Message)
}

object LibraryObject {
  def setCallback(callback: MessageCallback): Unit = {
    ???
  }

  def removeCallback(callback: MessageCallback): Unit = {
    ???
  }

  def start(): Unit = {
    ???
  }
}

def messagesSource: Observable[Message] =
  Observable((subscriber: Subscriber[Message]) ⇒ {
    val callback = new MessageCallback {
      def onMessage(message: Message) {
        subscriber.onNext(message)
      }
    }
    LibraryObject.setCallback(callback)
    subscriber.add {
      LibraryObject.removeCallback(callback)
    }
  })

As for the blocking/non-blocking start(): Usually callback-based architecture separates callback subscription and the process start. In that case, you can create as many messageSources as you want completely independently of when you start() the process. Also the decision whether you fork it or not is completely upon you. Is your architecture different from this?

You should also handle finishing the process somehow. The best would be to add an onCompleted handler to the MessageCallback interface. If you want to handle errors, also add an onError handler. Now behold, you have just declared the fundamental building stone of RxJava, an Observer :-)

Tomáš Dvořák
  • 1,490
  • 1
  • 9
  • 20
  • what if the `MessageCallback` interface has multiple methods, how to convert it into a single observable? there can be only one `onNext` method and only one parameter – weima Aug 23 '17 at 16:33