Как мы используем runOnUiThread в Android?

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

public class TestActivity extends Activity {

    Button btn;
    int i = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runThread();
            }
        });
    }

    private void runThread(){
        runOnUiThread (new Thread(new Runnable() {  
            public void run() {
                while(i++ < 1000){
                    btn.setText("#"+i);
                    try {
                        Thread.sleep(300);
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }
        }));
    }
}

person user1049280    schedule 21.06.2012    source источник


Ответы (10)


Ниже приведен исправленный фрагмент функции runThread.

private void runThread() {

    new Thread() {
        public void run() {
            while (i++ < 1000) {
                try {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            btn.setText("#" + i);
                        }
                    });
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}
person Vipul    schedule 21.06.2012
comment
Разве это не сбор мусора почти сразу? Вероятно, вам нужно сохранить некоторую ссылку на Thread() - person Nick; 21.06.2013
comment
@Nick: сборщик мусора также следит за стеком, т. Е. Когда поток запущен, он не будет подвергаться сборке мусора. - person Miro Kropacek; 13.03.2016
comment
@Vipul, у меня был вопрос о повороте телефона: я хочу, чтобы после поворота телефона этот поток запускался, и новый поток не создавался. Можете ли вы дать несколько советов о том, как предотвратить создание новой темы после поворота телефона? - person user1836957; 27.02.2018

Просто оберните его как функцию, а затем вызовите эту функцию из фонового потока.

public void debugMsg(String msg) {
    final String str = msg;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mInfo.setText(str);
        }
    });
}
person Community    schedule 16.01.2015
comment
проголосовал за то, что показал, как получить доступ к данным во внешней области видимости (final) - person mariotomo; 10.09.2018

У вас это задом наперед. Нажатие кнопки приводит к вызову runOnUiThread(), но в этом нет необходимости, поскольку обработчик кликов уже запущен в потоке пользовательского интерфейса. Затем ваш код в runOnUiThread() запускает новый фоновый поток, в котором вы пытаетесь выполнить операции с пользовательским интерфейсом, которые затем терпят неудачу.

Вместо этого просто запустите фоновый поток непосредственно из обработчика кликов. Затем оберните вызовы btn.setText() внутри вызова runOnUiThread().

person Graham Borland    schedule 21.06.2012
comment
Хотя обработчик кликов уже находится в потоке пользовательского интерфейса, вызов runOnUiThread() не нужен, но он должен быть безвредным. В Javadoc для этого метода указано, что выполняется указанное действие в потоке пользовательского интерфейса. Если текущий поток является потоком пользовательского интерфейса, то действие выполняется немедленно. Если текущий поток не является потоком пользовательского интерфейса, действие отправляется в очередь событий потока пользовательского интерфейса. - person k2col; 04.10.2017

Есть несколько техник использования runOnUiThread(), давайте посмотрим все

Это мой основной поток (поток пользовательского интерфейса) под названием AndroidBasicThreadActivity, и я собираюсь обновлять его из рабочего потока различными способами:

public class AndroidBasicThreadActivity extends AppCompatActivity
{
    public static TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_basic_thread);

        textView = (TextView) findViewById(R.id.textview);

        MyAndroidThread myTask = new MyAndroidThread(AndroidBasicThreadActivity.this);
        Thread t1 = new Thread(myTask, "Bajrang");
        t1.start();
    }
}

1.) Путем передачи экземпляра Activity в качестве аргумента рабочего потока

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {

        //perform heavy task here and finally update the UI with result this way - 
        activity.runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
            }
        });
    }
}

2.) С помощью метода post(Runnable runnable) View в рабочем потоке

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {
     //perform heavy task here and finally update the UI with result this way - 
       AndroidBasicThreadActivity.textView.post(new Runnable()
      { 
        @Override
        public void run()
        {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });

    }
}

3.) Используя класс Handler из пакета android.os Если у нас нет контекста (this/ getApplicationContext()) или экземпляра Activity (AndroidBasicThreadActivity.this), тогда мы должны использовать класс Handler, как показано ниже:

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
   public void run()
  {
  //perform heavy task here and finally update the UI with result this way - 
  new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });
  }
}
person Bajrang Hudda    schedule 16.02.2017
comment
Спасибо .. Вместо того, чтобы просто повторять об активности runOnUIThread, вы упомянули все возможные способы ее вызова. - person arun; 06.04.2019
comment
Спасибо .. Вместо того, чтобы просто повторять об активности runOnUIThread, вы упомянули все возможные способы ее вызова. - person arun; 06.04.2019

Если вы используете во фрагменте, просто напишите

getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // Do something on UiThread
    }
});
person Shivam Yadav    schedule 15.03.2019

Мы используем Worker Thread, чтобы сделать приложения более плавными и избежать ANR. Нам может понадобиться обновить пользовательский интерфейс после тяжелого процесса в рабочем Tread. Пользовательский интерфейс может быть обновлен только из потока пользовательского интерфейса. В таких случаях мы используем Handler или runOnUiThread, у обоих есть метод запуска Runnable, который выполняется в потоке пользовательского интерфейса. Метод onClick запускается в потоке пользовательского интерфейса, поэтому здесь не нужно использовать runOnUiThread.

Использование Котлина

Во время действия,

this.runOnUiThread {
      // Do stuff
}

Из Фрагмента,

activity?.runOnUiThread {
      // Do stuff
}

Использование Java,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});
person Varun Chandran    schedule 21.02.2020

твое это:

@UiThread
    public void logMsg(final String msg) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                Log.d("UI thread", "I am the UI thread");


            }
        });
    }
person Mr T    schedule 10.03.2017

Вы можете использовать из этого образца:

В следующем примере мы собираемся использовать это средство для публикации результатов поиска синонимов, которые были обработаны фоновым потоком.

Чтобы достичь цели во время обратного вызова действия OnCreate, мы настроим onClickListener для запуска searchTask в созданном потоке.

Когда пользователь нажимает кнопку «Поиск», мы создаем анонимный класс Runnable, который ищет слово, введенное в R.id.wordEt EditText, и запускает поток для выполнения Runnable.

Когда поиск завершится, мы создадим экземпляр Runnable SetSynonymResult, чтобы опубликовать результат обратно в синоним TextView через поток пользовательского интерфейса.

Этот метод иногда не самый удобный, особенно когда у нас нет доступа к экземпляру Activity; поэтому в следующих главах мы собираемся обсудить более простые и понятные методы обновления пользовательского интерфейса из фоновой вычислительной задачи.

public class MainActivity extends AppCompatActivity {

    class SetSynonymResult implements Runnable {
        String synonym;

        SetSynonymResult(String synonym) {
            this.synonym = synonym;
        }

        public void run() {
            Log.d("AsyncAndroid", String.format("Sending synonym result %s on %d",
                    synonym, Thread.currentThread().getId()) + " !");
            TextView tv = (TextView) findViewById(R.id.synonymTv);
            tv.setText(this.synonym);
        }
    }

    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button search = (Button) findViewById(R.id.searchBut);
        final EditText word = (EditText) findViewById(R.id.wordEt);
        search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Runnable searchTask = new Runnable() {
                    @Override
                    public void run() {
                        String result = searchSynomim(word.getText().toString());
                        Log.d("AsyncAndroid", String.format("Searching for synonym for %s on %s",
                                word.getText(), Thread.currentThread().getName()));
                        runOnUiThread(new SetSynonymResult(result));
                    }
                };
                Thread thread = new Thread(searchTask);
                thread.start();
            }
        });

    }

    static int i = 0;

    String searchSynomim(String word) {
        return ++i % 2 == 0 ? "fake" : "mock";
    }
}

Источник :

асинхронное программирование для Android Хелдер Васконселос

person Community    schedule 27.10.2016

Вот как я его использую:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });
person Josh Aquino    schedule 26.12.2016

Попробуйте это: getActivity().runOnUiThread(new Runnable...

Это потому что:

1) неявное this в вашем вызове runOnUiThread относится к AsyncTask, а не к вашему фрагменту.

2) Фрагмент не имеет runOnUiThread.

Тем не менее, Активность делает.

Обратите внимание, что Activity просто выполняет Runnable, если вы уже находитесь в основном потоке, в противном случае он использует обработчик. Вы можете реализовать обработчик в своем фрагменте, если не хотите беспокоиться о контексте этого, на самом деле это очень просто:

// Экземпляр класса

private Handler mHandler = new Handler(Looper.getMainLooper());

// где-нибудь еще в вашем коде

mHandler.post(<your runnable>);

// ^ это всегда будет выполняться в следующем цикле выполнения основного потока.

person Pradeep Kumar    schedule 26.11.2019