Search code examples
androidmultithreadingandroid-layoutandroid-activityjava.util.concurrent

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?


Solution

  • 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).