Пока цикл внутри потока не работает?

#android #multithreading #while-loop

#Android #многопоточность #цикл while

Вопрос:

У меня очень простой пользовательский интерфейс, и мне нужно постоянно запускать процесс проверки, поэтому я пытаюсь использовать поток с циклом while. Когда я запускаю цикл только с помощью команды Thread.sleep(1000), он работает нормально, но как только я ввожу display.setText() , программа запускается на секунду в эмуляторе, а затем завершается. Я даже не вижу сообщения об ошибке, поскольку оно так быстро завершается.

Затем я взял команду display.setText() вне потока и просто поместил ее непосредственно в onCreate, и она работает нормально (так что с фактической командой проблем нет).

вот мой код, и я буду признателен за помощь. Спасибо!

 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);



    on=(Button) findViewById(R.id.bon);
    off=(Button) findViewById(R.id.boff);
    display=(TextView) findViewById(R.id.tvdisplay);
    display2=(TextView) findViewById(R.id.tvdisplay2);
    display3=(TextView) findViewById(R.id.tvdisplay3);
    stopper=(Button) findViewById(R.id.stops);


    stopper.setOnClickListener(new View.OnClickListener() {


        @Override

        public void onClick(View v) {
            // TODO Auto-generated method stub
            if(boo=true)
            {
                boo=false;
                display3.setText("System Off");
            }
            else{
                boo=true;
            }
            }
    });





    Thread x = new Thread() {
        public void run() {
            while (boo) {
                 display3.setText("System On");

                try {
                    // do something here
                    //display3.setText("System On");

                    Log.d(TAG, "local Thread sleeping");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.e(TAG, "local Thread error", e);
                }

            }
        }
    };


    display3.setText("System On");
    display3.setText("System On");


    x.start();
}
 

Комментарии:

1. код, относящийся к потоку, был бы полезен.

2. Добро пожаловать в stackoverflow! Если вы найдете ответ полезным, вы можете проголосовать за него! Если вы чувствуете, что кто-то адекватно ответил на ваш вопрос, установите флажок рядом с ответом, чтобы принять его.

3. @AlanMoore, код добавлен с оригинальным сообщением

Ответ №1:

Вы не можете обновить пользовательский интерфейс из потока, отличного от пользовательского интерфейса. Используйте обработчик. Что-то вроде этого может сработать:

 // inside onCreate:
final Handler handler = new Handler();
final Runnable updater = new Runnable() {
    public void run() {
        display3.setText("System On");
    }
};

Thread x = new Thread() {
    public void run() {
        while (boo) {
            handler.invokeLater(updater);

            try {
                // do something here
                //display3.setText("System On");

                Log.d(TAG, "local Thread sleeping");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Log.e(TAG, "local Thread error", e);
            }

        }
    }
};
 

Вы также можете избежать обработчика для этого простого случая и просто использовать

         while (boo) {
            runOnUiThread(updater);
            // ...
 

В качестве альтернативы вы можете использовать AsyncTask вместо своего собственного класса Thread и переопределить onProgressUpdate метод.

Комментарии:

1. @user947659 Я добавил ссылку на документы обработчика, а также некоторые примеры кода.

2. Прежде чем я попробовал ваше решение, я установил команду display.setText внутри потока, как и в моем коде, но я просто удалил цикл while. Не могли бы вы сказать мне, почему это работает без цикла while? Он по-прежнему изменяет пользовательский интерфейс из потока, отличного от пользовательского интерфейса.

3. Вы не должны изменять пользовательский интерфейс из чего-либо, кроме потока пользовательского интерфейса; иногда это может сработать, но это зависит от удачи, и на него никогда не следует полагаться.

Ответ №2:

Не уверен на 100%, но я думаю, что это случай невозможности изменять элементы управления пользовательского интерфейса из потока, который их не создавал?

Ответ №3:

Когда вы не находитесь в своем потоке пользовательского интерфейса, вместо display3.setText("test") использования:

 display3.post(new Runnable() {
    public void run() {
        display3.setText("test");
    {
});
 

Комментарии:

1. Вам не нужно объявлять display3 как final, если это переменная экземпляра.

2. Но он объявил ‘display3’ в методе.

3. Он инициализировал его в методе. Я не вижу объявления TextView display3 в его коде.

Ответ №4:

Вместо этого вы должны инкапсулировать этот код в AsyncTask. Вот так:

 private class MyTask extends AsyncTask<Void, Void, Void> {
  private Activity activity;
  MyTask(Activity activity){
    this.activity = activity;
  }  

  protected Long doInBackground() {
    while (true){
      activity.runOnUiThread(new Runnable(){
        public void run(){
          display3.setText("System On");
        }
      });
      try{
        Thread.sleep(1000);
      }catch (InterruptedException e) {
        Log.e(TAG, "local Thread error", e);
      }
   }  
}
 

Затем просто запустите задачу из вашего метода onCreate.

Ответ №5:

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

  1. используйте обработчик
  2. используйте AsyncTask
  3. используйте LocalBroadcast
  4. если процесс является шаблоном наблюдателя, можно использовать RxJava