Обновить пользовательский интерфейс из потока

Я хочу обновить свой интерфейс из потока, который обновляет индикатор выполнения. К сожалению, при обновлении ничьей индикатора выполнения из "запускаемого" индикатор исчезает! Изменение прорисовки прогрессбаров в onCreate() с другой стороны работает!

Какие-либо предложения?

public void onCreate(Bundle savedInstanceState) {
    res = getResources();
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/
    handler.postDelayed(runnable, 1);       
}

private Runnable runnable = new Runnable() {
    public void run() {  
        runOnUiThread(new Runnable() { 
            public void run() 
            { 
                //* The Complete ProgressBar does not appear**/                         
                pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
            } 
        }); 
    }
}

7 ответов

Вы должны сделать это с помощью AsyncTask (интеллектуального фонового потока) и ProgressDialog

AsyncTask позволяет правильно и легко использовать поток пользовательского интерфейса. Этот класс позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и / или обработчиками.

Асинхронная задача определяется вычислением, которое выполняется в фоновом потоке и результаты которого публикуются в потоке пользовательского интерфейса. Асинхронная задача определяется 3 общими типами, называемыми Params, Progress и Result, и 4 шагами, называемыми begin, doInBackground, processProgress и end.

4 шага

Когда асинхронная задача выполняется, задача проходит 4 шага:

onPreExecute(), вызывается в потоке пользовательского интерфейса сразу после выполнения задачи. Этот шаг обычно используется для настройки задачи, например, показывая индикатор выполнения в пользовательском интерфейсе.

doInBackground(Params...), вызывается в фоновом потоке сразу после завершения выполнения onPreExecute(). Этот шаг используется для выполнения фоновых вычислений, которые могут занять много времени. Параметры асинхронной задачи передаются на этот шаг. Результат вычисления должен быть возвращен этим шагом и будет возвращен к последнему шагу. Этот шаг также может использовать publishProgress(Progress...) для публикации одной или нескольких единиц прогресса. Эти значения публикуются в потоке пользовательского интерфейса на шаге onProgressUpdate(Progress...).

onProgressUpdate(Progress...), вызывается в потоке пользовательского интерфейса после вызова publishProgress(Progress...). Время выполнения не определено. Этот метод используется для отображения любой формы прогресса в пользовательском интерфейсе, пока фоновые вычисления все еще выполняются. Например, его можно использовать для анимации индикатора выполнения или отображения журналов в текстовом поле.

onPostExecute(Result), вызывается в потоке пользовательского интерфейса после завершения фоновых вычислений. Результат фонового вычисления передается на этот шаг в качестве параметра. Правила потоков

Для правильной работы этого класса необходимо соблюдать несколько правил многопоточности:

Экземпляр задачи должен быть создан в потоке пользовательского интерфейса. execute(Params...) должен быть вызван в потоке пользовательского интерфейса. Не вызывайте onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) вручную. Задача может быть выполнена только один раз (исключение будет выдано, если будет предпринята вторая попытка).

Пример кода
То, что делает адаптер в этом примере, не важно, более важно понимать, что вам нужно использовать AsyncTask для отображения диалогового окна прогресса.

private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > {
    ProgressDialog dialog;
    @Override
    protected void onPreExecute() {
        dialog = new ProgressDialog(viewContacts.this);
        dialog.setMessage(getString(R.string.please_wait_while_loading));
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);
        dialog.show();
    }
    /* (non-Javadoc)
     * @see android.os.AsyncTask#doInBackground(Params[])
     */
    @Override
    protected ContactsListCursorAdapter doInBackground(Void... params) {
        cur1 = objItem.getContacts();
        startManagingCursor(cur1);

        adapter1 = new ContactsListCursorAdapter (viewContacts.this,
                R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {});

        return adapter1;
    }

    protected void onPostExecute(ContactsListCursorAdapter result) {
        list.setAdapter(result);
        dialog.dismiss();
    }
}

Самое простое решение, которое я видел для предоставления короткого исполнения потоку пользовательского интерфейса, - это использование метода post () представления. Это необходимо, поскольку методы пользовательского интерфейса не являются повторными. Метод для этого:

package android.view;

public class View;

public boolean post(Runnable action);

Метод post () соответствует SwingUtilities.invokeLater (). К сожалению, я не нашел чего-то простого, что соответствует SwingUtilities.invokeAndWait (), но можно построить более позднее на основе первого с монитором и флагом.

Таким образом, вы экономите на этом, создавая обработчик. Вам просто нужно найти свое мнение, а затем опубликовать на нем. Вы можете найти свое представление через findViewById (), если вы склонны работать с идентифицированными ресурсами. Полученный код очень прост:

/* inside your non-UI thread */

view.post(new Runnable() {
        public void run() {
            /* the desired UI update */
        }
    });
}

Примечание. По сравнению с SwingUtilities.invokeLater () метод View.post () возвращает логическое значение, указывающее, имеет ли представление связанную очередь событий. Так как я использовал invokeLater () соотв. post () в любом случае только для огня и забыть, я не проверял значение результата. По сути, вы должны вызывать post () только после вызова onAttachedToWindow () в представлении.

С уважением

Используйте класс AsyncTask (вместо Runnable). У него есть метод onProgressUpdate, который может влиять на пользовательский интерфейс (он вызывается в потоке пользовательского интерфейса).

Если вы используете Handler (Я вижу, вы делаете, и, надеюсь, вы создали его экземпляр в потоке пользовательского интерфейса), то не используйте runOnUiThread() внутри вашего runnable, runOnUiThread() используется, когда вы делаете что-то из не-пользовательского потока, однако Handler уже выполнит ваш runnable в пользовательском интерфейсе

Попробуйте сделать что-то вроде этого:

private Handler mHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    res = getResources();
    // pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works**
    mHandler.postDelayed(runnable, 1);
}

private Runnable runnable = new Runnable() {
    public void run() {
        pB.setProgressDrawable(getResources().getDrawable(R.drawable.green));
        pB.invalidate(); // maybe this will even not needed - try to comment out
    }
};

Вам нужно создать Handler в потоке пользовательского интерфейса, а затем использовать его для публикации или отправки сообщения из другого потока для обновления пользовательского интерфейса

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

Я бы использовал AsyncTask tho, однако шаблон наблюдателя может быть хорош по причинам настройки, плюс его легче понять. Также я не уверен, если этот способ широко принят или будет работать на 100%. Я скачиваю и Android-плагин, чтобы проверить это

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

HandlerThread является одной из альтернатив Thread или же AsyncTask, Если вам нужно обновить интерфейс от HandlerThread, опубликовать сообщение в теме пользовательского интерфейса Looper и обработчик потока пользовательского интерфейса может обрабатывать обновления пользовательского интерфейса.

Пример кода:

Android: тост в потоке

Другие вопросы по тегам