12

I'm using ListenableFuture from Guava, and one nice thing about them is that one pass Executor to the Futures.addCallback method, that is, ask to execute the callback on a given thread/executor.

In my Android application, I want to be able to start the asynchronous execution based on ListenableFuture in the UI thread, and schedule a callback which is also executed also on the UI thread. Therefore, I'd like to somehow submit the UI thread executor to the Futures.addCallback method mentioned above. How to achieve that?

Or, in other words, I want to have an executor for the UI thread. Is it available already in Android, or, if I have to create my own, how do I do that?

EDIT: As an extension to this question, is it possible to do same thing, but not just with UI thread, but with any particular thread, where the call to async method is made?

I would be happy to know how to achieve the same effect without resorting to the Android-specific stuff like Handler and Looper, just with pure Java.

Community
  • 1
  • 1
Haspemulator
  • 11,050
  • 9
  • 49
  • 76

5 Answers5

25

I think I've see some implementation doing that. The basic Idea is roughly

class UiThreadExecutor implements Executor {
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    @Override
    public void execute(Runnable command) {
        mHandler.post(command);
    }
}

You can delegate to run anything in the main thread by passing it to a handler for the main thread.

Edit: https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/android/MainThreadExecutor.java for example

Edit2: You can configure the handler like e.g. SensorManager#registerListener(..., Handler handler) allows you to do.

class HandlerThreadExecutor implements Executor {
    private final Handler mHandler;
    public HandlerThreadExecutor(Handler optionalHandler) {
        mHandler = optionalHandler != null ? optionalHandler : new Handler(Looper.getMainLooper());
    }

    @Override
    public void execute(Runnable command) {
        mHandler.post(command);
    }
}

The advantage over using the current thread's looper is that it makes it explicit which Looper you use. In your solution you take the Looper of whatever thread calls new ExecuteOnCaller() - and that's often not the thread you run code in later.

I would be happy to know how to achieve the same effect without resorting to the Android-specific stuff like Handler and Looper, just with pure Java.

Looper, Handler and the message queue behind all that logic are made of mostly pure Java. The problem with a generic solution is that you can't "inject" code to run into a thread. The thread must periodically check some kind of task queue to see if there is something to run.

If you write code like

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                System.out.println("Hello");
            }
        }
    }).start();

Then there is no way to make that thread do anything else but constantly print "Hello". If you could do that it would be like dynamically inserting a jump to other code into the program code. That would IMO be a terrible idea.

    final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    Runnable codeToRunInThisThread = queue.take();
                    codeToRunInThisThread.run();
                }
            } catch (InterruptedException ignored) {}
        }
    }).start();

On the other hand is a simple thread that loops forever on a queue. The thread could do other tasks in between but you have to add a manual check into the code.

And you can send it tasks via

    queue.put(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello!");
        }
    });

There is no special handler defined here but that's the core of what Handler & Looper do in Android. Handler in Android allows you to define a callback for a Message instead of just a Runnable.

Executors.newCachedThreadPool() and similar do roughly the same thing. There are just multiple threads waiting on code in a single queue.


As an extension to this question, is it possible to do same thing, but not just with UI thread, but with any particular thread, where the call to async method is made?

The generic answer is No. Only if there is a way to inject code to run in that thread.

zapl
  • 63,179
  • 10
  • 123
  • 154
  • What about http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)? – Fildor Jan 21 '14 at 11:12
  • @Fildor that's the same but requires to have a reference to an `Activity` which you should not need to have within that executor. – zapl Jan 21 '14 at 11:13
  • Thanks, this definitely answers the question. I'd like to know more, though: is it possible to capture and later execute on the current thread, no matter if it's UI thread or not? I'm coming from C#, and I know the way to do it there, would be nice to learn same thing for Java. – Haspemulator Jan 21 '14 at 12:49
  • @Haspemulator Android's handler (they don't exist in standard Java) have methods like `mHandler.postDelayed(command, timeInMilliseconds)` where you can have something execute later. By "current thread" you mean the thread the calls `Executor.execute(Runnable)` or the thread that submitted the `ListenableFuture` initially? – zapl Jan 21 '14 at 13:54
  • @zapl I mean the thread where `ListenableFuture` was created. For me usually it's the UI thread, which calls some async operation from the model and want to present that operation's result in the UI. – Haspemulator Jan 21 '14 at 14:10
  • @Haspemulator Your solution works fine as long as you do `new ExecuteOnCaller()` in the same thread you do `executor.submit(...)`. I've also added some pieces of information how you could have such a Handler thing in pure Java. Have a look into the [source](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/Looper.java) there is not much magic there. – zapl Jan 21 '14 at 14:25
  • Okay, thanks for the explanation. I thought every Java thread could have similar mechanism, hence the question. – Haspemulator Jan 21 '14 at 14:29
  • You can make every thread check but no thread will do that on it's own. – zapl Jan 21 '14 at 14:32
  • I edited my answer to illustrate how I would use my `ExecuteOnCaller` class, and why it makes sense to implement it like I did. Of course, the illustration is only taking into account the UI thread, but the idea is same: you want callback in the same thread where the async operation was initiated. – Haspemulator Jan 21 '14 at 14:37
  • Looks good :) (Although you typically don't have threads other than the UI thread that have a looper while they [can be useful](http://stackoverflow.com/questions/13368762/android-anrs-from-code-running-in-a-handler/13369215#13369215)) – zapl Jan 21 '14 at 14:42
  • 1
    You are my hero. Thanks. – Richard Clayton Jan 27 '16 at 17:44
9

Based on asnwer from @zapl, here is my implementation, which also answers the edited (extended) question: https://gist.github.com/RomanIakovlev/8540439

Figured out I'll also put it here, in case if link will rot some day:

package com.example.concurrent;

import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Executor;

/**
* When the calling thread has a Looper installed (like the UI thread), an instance of ExecuteOnCaller will submit
* Runnables into the caller thread. Otherwise it will submit the Runnables to the UI thread.
*/
public class ExecuteOnCaller implements Executor {

    private static ThreadLocal<Handler> threadLocalHandler = new ThreadLocal<Handler>() {
        @Override
        protected Handler initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null)
            looper = Looper.getMainLooper();
            return new Handler(looper);
        }
    };

    private final Handler handler = threadLocalHandler.get();

    @Override
    public void execute(Runnable command) {
        handler.post(command);
    }
}

My pattern to use it would be like this:

/**
* in SomeActivity.java or SomeFragment.java
*/
Futures.addCallback(myModel.asyncOperation(param), new FutureCallback<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            // handle success
        }

        @Override
        public void onFailure(Throwable throwable) {
            // handle exception
        }
    }, new ExecuteOnCaller());
Haspemulator
  • 11,050
  • 9
  • 49
  • 76
6

Use com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD.

An Executor that uses the main application thread.

Source: Android docs

The tasks APIs are part of Google Play services since version 9.0.0.

Niels
  • 725
  • 5
  • 14
  • +1, although this requires to use the Google Play services. On the other hand, it seems this API (Tasks) is solving a lot of problems with `ListenableFuture`, particularly about Activity lifecycle issues (but curiously not the Fragment lifecycle issues). – Haspemulator Jul 12 '16 at 12:30
4

For Android UI thread executor use:

ContextCompat.getMainExecutor(context)

solidogen
  • 591
  • 5
  • 12
0

To address your question and extended question to create an Executor that simply runs on the current thread and avoids Android classes:

class DirectExecutor implements Executor {
   public void execute(Runnable r) {
     r.run();
   }
 }

See documentation: https://developer.android.com/reference/java/util/concurrent/Executor

keith
  • 3,105
  • 2
  • 29
  • 34