Запуск другого потока не позволяет отображать действие

#android #multithreading #android-layout #android-activity #java.util.concurrent

#Android #многопоточность #android-layout #android-активность #java.util.concurrent

Вопрос:

У меня есть этот фрагмент действия:

 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();
        }
    }
}
  

Где MyExternal — это класс в другой библиотеке.

Метод DecodeAndSend выглядит примерно так:

 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;
}
  

Наконец, ServerConnection класс реализует Callable<String> , поэтому я показываю вам call метод:

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

    return "OK";
}
  

Вызов Thread.sleep(2000); на самом деле является вызовом веб-сервера для отправки некоторых данных.

Проблема в том, что ResultActivity не показывает свой макет, пока call вызов не вернется.

Чего не хватает в этом коде?

Ответ №1:

DecodeAndSend вызывается из основного потока. Он вызывает, Future.get() который ожидает завершения задания, поэтому он блокирует основной поток. Вы также должны вызвать этот метод из фонового потока. Я думаю, было бы неплохо отправить его в тот же пул потоков, поскольку он отправляется после первого задания, которого он будет ждать.

Вы не можете вернуть что-либо о результатах запроса с помощью этого метода, потому что он асинхронный.

 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
    });
}
  

Однако ваш ServerConnection класс уже принимает caller параметр, поэтому, вероятно, он должен просто обрабатывать сам обратный вызов. И в зависимости от того, что вы делаете в обратном вызове, вы можете захотеть опубликовать обратные вызовы в основном потоке.

Кстати, в Java принято всегда начинать имена методов со строчной буквы (верблюжий регистр).

Комментарии:

1. Если я использую caller inside ServerConnection для уведомления о статусе задачи, куда я могу позвонить pool.shutdown ? P.S. Я пришел из мира C #, и даже когда C # также использует регистр верблюда в качестве стандарта, имена методов должны начинаться с верхнего регистра. Я изменю все имена методов в соответствии с соглашением Java.

2. Вы можете вызвать его сразу после pool.submit() вызова. Уже отправленные задания по-прежнему выполняются до завершения после вызова shutDown() . (Я также использую C #. Одна из неприятных вещей в использовании нескольких языков — это разница в соглашении. Я лично предпочитаю способ Java, потому что он лучше отличает функции от конструкторов, как на сайте объявления, так и на сайте вызова.)

Ответ №2:

Feature.get() это блокирующий вызов. Поток пользовательского интерфейса заблокирован в ожидании возврата этого вызова, поэтому не может позаботиться о рисовании вашего макета. Попробуйте передать прослушиватель результатов ResultListener в ServerConnection и используйте два обратных вызова для соответствующего обновления вашего пользовательского интерфейса

Ответ №3:

Future.get() является блокирующим вызовом — выполнение останавливается до получения результата

Результат может быть получен только с помощью метода get после завершения вычисления, при необходимости блокируя, пока он не будет готов.

Итак, ваш Activity onCreate метод вызывает этот материал, а затем блокирует до тех пор, пока call (который выполняется в другом потоке) не вернет свой результат. Так onCreate не завершается, и макет не завершается.

Если вы хотите использовать этот блокирующий код, но после того, как представление выложено, я бы использовал другую часть жизненного цикла действия, например onStart (установите флаг, чтобы запускать его только один раз!). В противном случае вам нужно будет использовать какой-либо другой метод параллелизма, чтобы получить свой результат и использовать его. Это зависит от того, что вы на самом деле делаете с результатом вашей call функции