Skip to content
Snippets Groups Projects
AndroidExecutorImpl.java 2.04 KiB
Newer Older
package org.briarproject.android;

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.inject.Inject;

import org.briarproject.api.android.AndroidExecutor;
akwizgran's avatar
akwizgran committed

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

class AndroidExecutorImpl implements AndroidExecutor {

	private static final int SHUTDOWN = 0, RUN = 1;
	private static final Logger LOG =
			Logger.getLogger(AndroidExecutorImpl.class.getName());

	private final Runnable loop;
	private final AtomicBoolean started = new AtomicBoolean(false);
	private final CountDownLatch startLatch = new CountDownLatch(1);

	private volatile Handler handler = null;

	@Inject
	AndroidExecutorImpl() {
		loop = new Runnable() {
			public void run() {
				Looper.prepare();
				handler = new FutureTaskHandler();
				startLatch.countDown();
				Looper.loop();
			}
		};
	}

	private void startIfNecessary() {
		if(started.getAndSet(true)) return;
		new Thread(loop, "AndroidExecutor").start();
		try {
			startLatch.await();
		} catch(InterruptedException e) {
			LOG.warning("Interrupted while starting executor thread");
			Thread.currentThread().interrupt();
		}
	}

	public <V> V call(Callable<V> c) throws InterruptedException,
	ExecutionException {
		startIfNecessary();
		Future<V> f = new FutureTask<V>(c);
		Message m = Message.obtain(handler, RUN, f);
		handler.sendMessage(m);
		return f.get();
	}

	public void shutdown() {
		if(handler != null) {
			Message m = Message.obtain(handler, SHUTDOWN);
			handler.sendMessage(m);
		}
	}

	private static class FutureTaskHandler extends Handler {

		@Override
		public void handleMessage(Message m) {
			switch(m.what) {
			case SHUTDOWN:
				Looper.myLooper().quit();
				break;
			case RUN:
				((FutureTask<?>) m.obj).run();
				break;
			default:
				throw new IllegalArgumentException();
			}
		}
	}
}