2016-11-24 7 views
0

Учитывая простой код, как это:почему UI процессы до тяжелого процесса не выполняются первый

public class MainActivity extends Activity { 
    @Override 
    public void onCreate(Bundle savedInastanceState){ 
     super.onCreate(savedInastanceState); 
     setContentView(R.layout.main); 
     final ProgressDialog pdUpdate = new ProgressDialog(this); 
     final Button btn = (Button)findViewById(R.id.btnUpdate); 
     btn.setOnClickListener(new View.OnClickListener(){ 
      @Override 
      public void onClick(View view){ 
       btn.setText(Calendar.getInstance().getTime().toString()); 
       int i=0; 
       while (i<100){ 
        i++; 
        try { 
         Thread.sleep(100); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
        Log.i("test","i = " + i); 
       } 
      } 
     }); 

    } 
} 

, нажав на btn две вещи должны произойти: изменение текста кнопки и подсчета i 100 с задержками 100 мсек (это просто пример симуляции тяжелого процесса, такого как чтение файла, загрузка и т. д.).
Я знаю, что правильный способ реализации таких кодов - использовать AsyncTask, но мой вопрос о том, как этот код скомпилирован. Это приложение с одним потоком. Поэтому компилятор сначала читает btn.setText(Calendar.getInstance().getTime().toString()); и переходит к следующим строкам кода только после выполнения этой строки (пожалуйста, исправьте меня, если я ошибаюсь), но этого не происходит. Зачем?
В C# существует метод Refresh(), который решает эту проблему (просто вызовите его после изменения пользовательского интерфейса и внесения изменений). Есть ли какой-либо подобный метод в java?
Я ценю любую помощь.

РЕДАКТИРОВАТЬ 1
есть речь идет о порядке следующих процессов:
Процесс 1:

btn.setText(Calendar.getInstance().getTime().toString()); 

Способ 2:

    i++; 
        try { 
         Thread.sleep(100); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
        Log.i("test","i = " + i); 
       } 

Независимо от того, какой из них приходит первым (Я имею в виду либо btn.onClick(){process1;process2;}, либо btn.onClick(){process2;process1;}), всегда выполняется второй процесс. В первую очередь я вижу подсчет i в Logcat, а затем вижу изменение текста кнопки.

+0

Вы спали в общей сложности 9,9 секунды. Я не знаю, почему вам нужно знать, как скомпилирован код, если вы не планируете писать компиляционную библиотеку, такую ​​как lambdas или injection. Я не уверен, что вы на самом деле спрашиваете. –

+1

Он не имеет ничего общего с компиляторами, но поток UI ... вы устанавливаете текст и блокируете поток пользовательского интерфейса ... поэтому явно не было времени перерисовать ... в C# есть 'Application.DoEvents();' но это плохой выбор человека ... – Selvin

+0

Я ожидаю увидеть изменение пользовательского интерфейса, а затем подсчитаю 'i' в' Logcat'. Но случается так, что первый подсчет, а затем изменение пользовательского интерфейса. – Behy

ответ

2

В этом случае не нужно называть эквивалент refresh(). Однако Button.setText() не перерисовывают иерархию представлений автоматически. Вместо этого он передает иерархию представления информацию о том, что текст был изменен и что его нужно перерисовать.В конечном итоге эта информация достигает корня иерархии представлений, которая, в свою очередь, сообщает Choreographer. В Choreographer график чертежа для следующего кадра. Это, в свою очередь, сохраняется в очереди сообщений потока пользовательского интерфейса.

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

считает также, что такие методы, как Handler.post(Runnable), View.post(Runnable) и их postDelayed аналогов могут служить в качестве альтернативы AsyncTask, если не нужно делать тяжелое вычисление, но вместо того, чтобы планировать некоторые операции (например, обновление) вида для последующего использования. Они используют тот же механизм, что и описанный выше, - помещают Runnable в очередь сообщений потока, которая, в свою очередь, подбирается и выполняется, когда поток простаивает.

+0

Спасибо, я думаю, что получил свой ответ. Но возможно ли изменить приоритет или порядок сообщений в очереди или это очередь FIFO? потому что, как я уже упоминал, в C# можно добавить 'btn.Refresh()' после текста настройки. Он меняет текст перед сном. – Behy

+0

@Behy Я уверен, что нет публичного API, позволяющего это сделать. Проверьте документацию «Хореографа» здесь: https://developer.android.com/reference/android/view/Choreographer.html –

+0

@ Marc Plano-Lesay Возможно, есть еще один механизм в C# (я googling), или он на самом деле не один поток (отдельный поток отвечает за обновление пользовательского интерфейса, например). Но это происходит. Я имею в виду, что Refresh() вызывает перерисовку пользовательского интерфейса перед сном. – Behy

-1

Android управляет своими циклами рисования. Но вы делитесь с ним основным потоком. Вы можете позвонить по номеру invalidate() в представлении, которое планирует перерисовку представления. Но вам придется освободить некоторое время на основной теме для Android, чтобы обновить интерфейс (в любом случае, setText(), вероятно, звонит invalidate()).

+0

Я просто спрашиваю, почему отрицательная точка? это неправильно? если правильные циклы рисования верны (я собираюсь google it), это звучит логически – Behy

+0

@Behy Глядя на другие ответы, я думаю, мы никогда не узнаем, так как мой выглядит правильно, хотя и свет. –

0

nonono, только Ваш конец метод запуска она может работать (Exp: просто спать 100 ЦКМ без цикла While) .Вы можете найти в гугле, как работает метод Java (мой английский очень плохо)

0

Целых. люди указали в комментариях, так как вы делаете свой цикл while в пределах onClickListener, нить пользовательского интерфейса никогда не получает шанс на вашу кнопку и, таким образом, перерисовывает указанную кнопку.

Я бы подумал, что если бы вы хотели обойти это, вы могли бы просто иметь другого слушателя, связанного с завершением процесса перерисовывания. Взгляните на следующие из этого answer:

public class DrawListenerView extends View{ 
    private Callback callback; 
    public DrawListenerView(Callback callback){ 
     this.callback = callback; 
    } 

    @Override 
    protected void onDraw (Canvas canvas){ 
     super.onDraw(canvas); 

     //add your method here you want to call 
     //Or use a Callback-pattern 
     callback.finish(); 
    } 
} 

public interface Callback(){ 
    public void finish(); 
} 

Вы можете сделать ваш петельные спать после того, как розыгрыш вашей кнопки завершена. Это даст вам новый текст, а затем последует.

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

 Смежные вопросы

  • Нет связанных вопросов^_^