jstuardo
jstuardo

Reputation: 4373

Running a different thread does not allow the activity to be displayed

I have this piece of an activity:

public class ResultActivity extends AppCompatActivity implements ResultListener {
    private String code = "";
    private String data = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        try {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_result);

            code = intent.getStringExtra("code");
            data = intent.getStringExtra("data");

            MyExternal.DecodeAndSend(this, code, data);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Where MyExternal is a class in other library.

The method DecodeAndSend is something like this:

public static boolean DecodeAndSend(ResultListener caller, String codigo, String data)
{
    try {
        ExecutorService pool = Executors.newFixedThreadPool(1);

        HashMap<String,String> arguments = new HashMap<>();

        Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));

        String status = resultado.get();
        if (status.equals("OK"))
            caller.OnSuccess();
        else
            caller.OnError(status);

        pool.shutdown();

        return true;
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    return false;
}

Finally, ServerConnection class implements Callable<String> so I show you the call method:

@Override
public String call() throws Exception {
    Thread.sleep(2000);
    

    return "OK";
}

The call to Thread.sleep(2000); is actually a call to a web server to send some data.

The problem is that the ResultActivity does not show its layout until the call call returns.

What is missing in this code?

Upvotes: 0

Views: 31

Answers (3)

Tenfour04
Tenfour04

Reputation: 93581

DecodeAndSend is called from the main thread. It calls Future.get() which waits for the job to finish, so it's blocking the main thread. You should call this method from a background thread as well. I think it would be okay to send it to your same thread pool since it is submitted after the first job that it will wait for.

You cannot return anything about the request results from this method, because it is asynchronous.

public static void DecodeAndSend(ResultListener caller, String codigo, String data)
{
    ExecutorService pool = Executors.newFixedThreadPool(1);

    HashMap<String,String> arguments = new HashMap<>();

    Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));

    pool.submit(new Runnable() {
        public void run () {
            try {
                String status = resultado.get();
                if (status.equals("OK"))
                    caller.OnSuccess();
                else
                    caller.OnError(status);

                pool.shutdown();
                return;
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            caller.OnError(null); // No status, only an exception
    });
}

However, your ServerConnection class already takes a caller parameter, so it should probably just handle the callback itself. And depending on what you're doing in the callback, you might want to post the callback calls to the main thread.

By the way, convention in Java is to always start method names with a lower-case letter (camel case).

Upvotes: 1

Blackbelt
Blackbelt

Reputation: 157437

Feature.get() is a blocking call. The UI Thread is blocked waiting for that call to return, hence can't take care of drawing your layout. Try passing the result listener to ResultListener to the ServerConnection and use the two callbacks to update your UI accordingly

Upvotes: 1

cactustictacs
cactustictacs

Reputation: 19524

Future.get() is a blocking call - execution stops until the result arrives

The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.

So your Activity's onCreate method calls that stuff, and then blocks until call (which is running on another thread) returns its result. So onCreate doesn't finish, and the layout doesn't complete.

If you want to use that blocking code, but after the view has laid out, I'd use another part of the Activity lifecycle like onStart (set a flag so you only run it once!). Otherwise you'll need to use some other concurrency technique to get your result and use it. It depends on what you're actually doing with the result of your call function

Upvotes: 0

Related Questions