How to properly implement camera2 realtime frame processing using RxJava? - android

I'm making reactive wrapper over camera2, my goal is to get each frame and then pass to face recognition.
So, I created a wrapper method over setOnImageAvailableListener
fun createOnImageAvailableFlowable(imageReader: ImageReader, handler: Handler): Flowable<ImageReader> {
return Flowable.create({ subscriber ->
imageReader.setOnImageAvailableListener({
if (!subscriber.isCancelled)
subscriber.onNext(it)
}, handler)
subscriber.setCancellable {
imageReader.setOnImageAvailableListener(null, null)
}
}, BackpressureStrategy.LATEST)
}
Reactive chain looks as follows:
createOnImageAvailableFlowable(imageReader!!, null)
.concatMap {
it.acquireLatestImage()?.use { image ->
val rotation = ReactiveCamera.getRotationCompensation(cameraId!!, this, applicationContext)
val visionImage = FirebaseVisionImage.fromMediaImage(image, rotation)
firebaseFaceDetector
.detectInImage(visionImage)
.toFlowable(BackpressureStrategy.LATEST)
.map { list ->Optional(list)}
} ?: Flowable.just(Optional(null))
}
...
This code works, but cause some lags on preview surface because all work performed in the main thread. This needs to be performed in separate thread. My naive solution is to add observeOn operator before concatMap:
createOnImageAvailableFlowable(imageReader!!, null)
.observeOn(Schedulers.io()) // doesn't switch thread
.concatMap {
// still main thread
}
...
But it doesn't affect, all work still in the main thread. If I specify concatMapEager instead of concatMap, all works as expected in separate thread, but the frames comes with a significant delay.
What I'm doing wrong? How can I instruct the reactive stream to be performed in a separate thread in this case? How can backpressure be handled in case of realtime frame processing?
Upd
I provided my own thread as Kiskae suggested, but now, only first emission happens in scheduler's thread, but the rest emissions remain in the main thread:
createOnImageAvailableFlowable(imageReader!!, null)
.subscribeOn(AndroidSchedulers.from(nonMainThread.looper))
.concatMap {
val t = Thread.currentThread()
val name = t.name
Log.d(TAG, "current thread {$name}")
...
}
Output:
D/MainActivity: current thread {Camera2}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}

Looking at the documentation of ImageReader.setOnImageAvailableListener:
Handler: The handler on which the listener should be invoked, or null if the listener should be invoked on the calling thread's looper.
Since you're subscribing on the main looper it ends up setting up the callback using the main looper, this causes all the processing before the concatMap to always occur on the application thread.
You can solve this by either providing a handler instead of null or calling subscribeOn and providing a handler-based scheduler like RxAndroid's HandlerScheduler.

Related

Make Main Thread to Wait for coroutine kotlin

I am trying to compress an image in android but and I don't want to use runBlocking.
How can I make the main UI thread to wait for the compression to take place?
Currently, I am doing this
var compressedImage: File? = null
runBlocking {
compressedImage = Compressor.compress(this#UpdateProfileActivity, cachedFile.absoluteFile)
}
camera = "gallery"
//doing something with the compressedImage.
How to do it without runBlocking?
You should not make the main-thread to wait for task completion. It causes freezing of your app until the main-thread gets free. You can do your long-running job in another thread, then switch to the main-thread to do whatever you want.
var compressedImage: File? = null
CoroutineScope().launch(Dispatchers.IO) {
compressedImage = Compressor.compress(this#UpdateProfileActivity, cachedFile.absoluteFile)
withContext(Dispatchers.Main) {
camera = "gallery"
// doing something with the compressedImage.
}
}

How to lock thread correctly with ReentrantLock or Mutex?

I have an acync method with callback:
myAsyncMethod(params) {
handleResult(it)
}
I need to convert it to sync method (block and return result) like this:
val result = mySyncMethod(params)
handleResult(result)
I can't re-implement it by another way because this method provided by third-party library.
I am trying to use ReentrantLock:
fun mySyncMethod(params:Type) {
Log.d("tag", "1")
val result = null
val mutex = ReentrantLock()
myAsyncMethod(params) {
Log.d("tag", "3")
result = it
mutex.unlock()
}
Log.d("tag", "2")
mutex.lock()
Log.d("tag", "4")
return result
}
handleResult(mySyncMethod(params))
I wait that I should see 1, 2, 3, 4. But I get 1, 2, 4, 3 and null in handleResult. I am trying the same with mutex but with the same result. How to make it works?
P.S. Sure I can use synchronized, but in this case I have to use Object-variable additionally.
Here is my code first. I used a thread instead of your method to test myself.
fun main() {
val mutex = ReentrantLock(true)
val asyncCondition = mutex.newCondition()
mutex.lock()
try
{
print(1)
thread {
mutex.lock()
try
{
print(2)
}
finally
{
asyncCondition.signalAll()
mutex.unlock()
}
}
asyncCondition.await()
print(3)
}
finally
{
mutex.unlock()
}
}
Java's ReentrnatLock.lock method will block a thread when it is already locked on the only same thread. Since you are trying to lock an async thread from another thread, your code is not working. To block or release another thread, you may use a Condition like my code.
The reason why I am using try..finallystatement everywhere is that if an exception occurred after locking a thread, you are going to meet the infinity loop, so you must be careful about this case.
You may see these references.
Concurrency (ReentrantLock) in different threads
How do java.util.concurrent.locks.Condition work?
I expect that I should see 1, 2, 3, 4.
Then you misunderstood what ReentrantLock is actually for.
If mySyncMethod can be called from different threads, then after mutex.lock() is called, only one thread at a time can execute the code following mutex.lock() - the other threads executing this method have to wait. Once mutex.unlock() is called, one of the waiting threads will execute the code too and so on.
So, the thread owning the mutex forces the other threads to wait but its own flow isn't blocked. myAsyncMethod starts running after the outer method has completed already and hence result is still null.
I need to convert it to sync method (block and return result) like this:
According to your requirements, you need to block the current thread until the async task is complete. To achieve this, you could use Semaphore or CountDownLatch. For example, with Semaphore, your method could look as follows:
fun mySyncMethod(params:Type) {
Log.d("tag", "1")
val result = null
val semaphore = Semaphore(0)
myAsyncMethod(params) {
Log.d("tag", "3")
result = it
semaphore.release()
}
Log.d("tag", "2")
semaphore.acquireUninterruptibly()
Log.d("tag", "4")
return result
}

Does RxJava frequently switch threads with performance loss?

ref:https://github.com/ReactiveX/RxAndroid/issues/420
in this context:
//pseudo-code
websocket(Callback(data){
//websocket very frequent data in no main thread
Observable.just(data)
.observeOn(Schedulers.computation())
.subscribe(data -> {
//computation thread
map2Obj(data);
});
});
//computation
void map2Obj(data){
//....
then change to main thread
}
------------------the blow is ExecutorService implementation model-----------------------------------
in this context:
//pseudo-code
static ExecutorService mExecutorService;
static {
mExecutorService = Executors.newFixedThreadPool(8);
}
websocket(Callback(data){
//websocket very frequent data in no main thread。change to other compute thread to prevent block "the thread of getting data"。in run() execute map2Obj(data)
mExecutorService.execute(new NewFixThread(str));
});
//computation
void map2Obj(data){
//....
then change to main thread
}
RxJava is better or java Executors?why?
Thx!!!!!
It switches threads as often as you need it to.
And no more.
Your sample code will continually move data from the main thread to another thread. This will cause a performance loss of a few microseconds each time, depending on how and when threads have to be created.
Exactly as you told it to.

Operators after findallAsync().asFlowable() are running on the UI thread

I'm facing with a problem, rxjava operators are running in the UI thread.
I'm using findAllAsync() to get object asynchronously and using asFlowable() to treat them with rxjava operator.
realm.where(Specie.class)
.equalTo("fauna", true)
.findAllAsync().asFlowable()
.filter(new Predicate<RealmResults<Specie>>() {
#Override
public boolean test(RealmResults<Specie> species) throws Exception {
System.out.println("THREAD : " + Thread.currentThread().getId()); // Print 1
return species.isLoaded();
}
})
But in the realm rxjava example they are using observeOn(AndroidSchedulers.mainThread()) , it means the previous operators are running asynchronously otherwise it would be useless.
Link : https://github.com/realm/realm-java/blob/master/examples/rxJavaExample/src/main/java/io/realm/examples/rxjava/animation/AnimationActivity.java
disposable = realm.where(Person.class).findAllAsync().asFlowable()
.flatMap(persons -> Flowable.fromIterable(persons))
.zipWith(Flowable.interval(1, TimeUnit.SECONDS), (person, tick) -> person)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(person -> {
TextView personView = new TextView(AnimationActivity.this);
personView.setText(person.getName());
container.addView(personView);
});
How can I run operators after asFlowable() asynchronously ?
edit : How can I access RealmResults obtained on the UI thread on a
background thread ?
The execution on Schedulers.computation(), so they add observeOn(AndroidSchedulers.mainThread()) to go back to the main thread.
The Realm Async Query API handles the asynchronous evaluation of the query on its own, and the results of that query are passed back by Realm to the UI thread, that is when isLoaded() is true.
To get off the main thread, you can use observeOn(Schedulers.io()) for example.

Actor/message-passing within threaded Game "event loop"

I am building an Android application (using Scala 2.9) and am using a Thread that renders to a SurfaceView; this is for a game so it should update as often as it can. I imagine this issue is similar to other game "event loops" where the input comes from a different thread.
Here is a gross approximation of the current approach that relies on synchronization. It "works well enough", but I have general misgivings about having to use explicit synchronization and "tying up" the View/input thread.
View, "UI thread":
def View.onTouchEvent(e) { // on UI thread
Game.handleInput(e)
}
Game, "Game Thread":
def Game.handleInput(e) = S synchronized { // on UI thread
alterStateBasedOnInput
}
def Game.run () { // on Game thread
while (running) {
S synchronized {
doGameStuff
}
View.post(someStateRelayedViaRunnable)
yield
}
}
Instead of explicitly using synchronization, I'd like to have something like this:
def View.onTouchEvent(e) { // on UI thread
Game.sendMessage(e)
}
def Game.run () { // on Game thread
while (running) {
processMessage
doGameStuff
View.sendMessage(someState) // hopefully same as Game.sendMessage
yield
}
}
Now, this is relatively easy to implement manually using a ConcurrentLinkedQueue or similar, but I would really not like to reinvent the wheel here. In addition, it would be nice to use such an actor/queue to post-back to the UI as well - right now I am using Android's support for posting an (asynchronous) Runnable to the UI thread.
I've briefly looked at several different actor implementations (mainly standard Scala and the Scalaz) and some different Java "message passing" libraries such as Jetlang, but most seem to use implicit threads or a thread executor service. But, in my case I wish to [run the actor and] process messages at a specific time on a specific thread. For View.sendMessage the messages should also be processed on the UI thread, but timing is not as important and can piggyback off of the Runnable execution noted above.
Then, I guess my question is, given the above:
What would be a good - as in "efficient" and idiomatic - approach to feed data between these two threads?
(I am also willing to entertain the suggestion that I completely fail to understand Scala actors and/or Scalaz actors and/or other message passing libraries; Scalaz seems like it might be able to work as I envision, but is hard for me to follow.)
Well, while I would still like to know of a generic/reusable approach to the above, practicality calls. This can also be done by running a Looper on the game thread and then putting the game "event loop stuff" inside the IdleHandler, but I did not like that inversion ..
Here is how I have currently implemented it:
Game/Thread class:
var handler: Handler = _ // handler created on View thread
// Send Message to Looper that exists on View thread
// (Created implicitly for all UI threads.)
def publishEvent(event: OutputEvent) {
handler.obtainMessage(0, event).sendToTarget
}
protected val queue = new ConcurrentLinkedQueue[InputEvent]
def queueEvent(event: InputEvent) { // on UI thread
queue.add(event)
}
def processQueuedEvents() { // on game Thread
#tailrec
def processNextEvent {
val event = queue.poll
if (event ne null) {
doStuffWithInputEvent(event)
processNextEvent
}
}
processNextEvent
}
override def run() { // on game Thread
while (running) {
processQueuedEvents
doOtherGameStuff ~ call_publishEvent ~ etc
}
}
View class:
// Created on UI thread, passed to Game instance
// (The Looper will dispatch Messages to Handlers.)
val handler = new Handler {
override def handleMessage(m: Message) {
val event = m.obj
doStuffWithOutputEvent(event)
}
}
// on UI thread
override def onTouch(v: View, ev: MotionEvent): Boolean = {
// safely queued, will be processed at the start of each game loop
game.queueEvent(InputEvent(..))
}

Categories

Resources