Search code examples
javaandroidconcurrencyfuturetask

FutureTask Submitted to an Executor Doesn't Run


I've written a class, a series of instances of which are intended to be called from an AsyncTask, which will return a result from the method runReport(). It creates a worker thread just fine, but for some reason it then doesn't execute the Callable's call() method. What am I doing wrong?

//Problem: doStuff() never gets called, even though the worker thread gets created.

@Override
public ReportResult runReport() throws InterruptedException, ExecutionException {
    Callable<ReportResult> report = new Callable<ReportResult>() {

        @Override
        public ReportResult call() throws Exception {
            doStuff();
            ...
            return new ReportResult(varWrittenByMethod);
        }
    };
    FutureTask<ReportResult> result = new FutureTask<ReportResult>(report);

    //I tried a few of these ExecutorService factory methods, with the same result.
    //I only made my own ThreadFactory to verify the worker was created
    ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFact());
    es.submit(report);

    ReportResult finalResult = result.get();
    es.shutdownNow();
    return finalResult;
}


private class ThreadFact implements ThreadFactory{

    @Override
    public Thread newThread(Runnable r) {
        Log.d(TAG, "Created worker Thread");
        return new Thread(r);
    }

}

As far as I can tell, I have to do this as a FutureTask in its own Thread, because it needs to do the following (all of which apart from the return is inside doStuff() ):

  • Do heavy some synchronous setup (The AsyncTask keeps that off the UI thread)
  • Call Looper.prepare()
  • Register a listener
  • Call Looper.loop(), catch a few callbacks from the listener over a period of time.
  • Call Looper.myLooper().quit() inside the listener callback when I have enough datapoints
  • Return the result

I'm open to better ways to do this. I originally let the AsyncTask make this call, then ran Looper.loop() on its thread, but I couldn't process a queue of these objects since I needed to call Looper.myLooper.quit() from the listener before returning a result, which poisoned the thread's message queue irreversibly.


Solution

  • Your thread factory doesn't propagate the passed Runnable to the created thread. In your ThreadFactory, try:

    return new Thread(r);
    

    Also, you should be using the FutureTask returned by the submit method, not the one you created explicitly. E.g.

    FutureTask<ReportResult> result = es.submit(report);
    ReportResult finalResult = result.get();