#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
insideServerConnection
для уведомления о статусе задачи, куда я могу позвонить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
функции