Как мы используем 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();
                    }
                }
             }
        }));
    }
}

13 ответов

Решение

Ниже приведен исправленный фрагмент 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();
}

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

public void debugMsg(String msg) {
    final String str = msg;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mInfo.setText(str);
        }
    });
}

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

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

runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });

Существует несколько методов, использующих 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) в рабочем потоке

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

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

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

За fragmentиспользуйте это:

      requireActivity().runOnUiThread(() -> {
//your code logic
});

За activityиспользуйте это:

      runOnUiThread(() -> {
//your code logic
});

runOnUiThread используется таким образом, чтобы пользовательский интерфейс мог обновляться с помощью нашего фонового потока. Подробнее: https://www.tutorialspoint.com/how-do-we-use-runonuithread-in-android

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

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

Во время активности

this.runOnUiThread {
      // Do stuff
}

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

activity?.runOnUiThread {
      // Do stuff
}

Используя Java,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});

Твое это:

@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");


            }
        });
    }

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

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

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

2) У фрагмента нет runOnUiThread.

Однако Activity делает.

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

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

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

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

mHandler.post(<your runnable>);

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

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

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.success1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //dummy delay for 2 second
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //update ui on UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        gifImageView.setGifImageResource(R.drawable.success);
                    }
                });

            }
        }).start();

    }

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

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });

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

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

Чтобы достичь цели во время обратного вызова действия 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 Helder Vasconcelos

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