Используйте метод DoInBackground в классе, расширяющем AsyncTask, чтобы изменить текст TextView в Activity, который не является MainActivity

Я студент университета, разрабатывающий приложение для Android, которое должно отображать сообщения, которые устройство Android получает через TCP с сервера.

Класс ListenForMessage (расширяющий AsyncTask) открывает TCP-соединение и получает сообщения от сервера в рамках метода DoInBackground. Я могу распечатать сообщения с сервера в журнале, но если я попытаюсь отредактировать текст TextView, мое приложение выйдет из строя. Кажется, я не могу редактировать тексты TextView из метода DoInBackground, особенно потому, что класс не расширяет действие, в котором создается TextView.

У меня есть два действия, и я выполняю класс ListenForMessage в onCreate второго действия StreamPageMain (до этого я выполнял его из MainActivity, но это тоже не сработало). Я попытался сообщить методу, к какому действию принадлежит TextView, как вы можете видеть в моем коде, и я создал другой метод в классе «ListenForMessage» для установки текста, так что это не метод DoInBackground, пытающийся изменить текст, но ничего из этого не сработало.

Резюме: Я отметил место в коде, что идет не так. Что мне нужно сделать, так это взять строку с именем «message» и поместить эту строку в TextView с идентификатором «textView1», который является частью действия «StreamPageMain», а не MainActivity.

Я начинаю с этого метода в MainActivity при нажатии кнопки:

 public void stream(View V) {

    EditText IPText = (EditText) findViewById(R.id.editTextBox2);  //Extracting the IP from an EditTextView
    EditText portText = (EditText) findViewById(R.id.editTextBox3); //Extracting the port from an EditTextView

    String IP = IPText.getEditableText().toString();            //correcting the format to String
    Integer port = Integer.parseInt(portText.getEditableText().toString()); //correcting the format to Integer

    Intent i = new Intent(MainActivity.this, StreamPageMain.class);
    i.putExtra("IP", IP);
    i.putExtra("port", port);
    startActivity(i);
}

Запускается второе Activity StreamPageMain. Это работает.

import android.widget.VideoView;   

public class StreamPageMain extends Activity {

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

    Log.d("StreamingApp", "OnCreate is running.");

    //In this middle part I am implementing a videostream into the second 
    activity, but that does not affect anything        

    String IP = getIntent().getStringExtra("IP");
    Integer port = getIntent().getExtras().getInt("port");
    ListenForMessage listenForMessage = new ListenForMessage(StreamPageMain.this, IP, port);
    listenForMessage.execute();
}
}

Вы можете увидеть, как я передаю Activity StreamPageMain классу ListenForMessage здесь. Сам класс, соединение и все работает нормально, если я закомментирую отмеченную строку:

package comn.example.ezio.streamingapp;

import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class ListenForMessage extends AsyncTask<Void, Void, Void> {

public Activity executingActivity;
public String IP;
public Integer port;

public ListenForMessage(Activity activity, String IPString, Integer portNumber) {
    this.executingActivity = activity;
    IP = IPString;
    port = portNumber;    
}

@Override
protected Void doInBackground(Void... voids) {

    try (Socket socket = new Socket(IP, port)) {

        Boolean connectionCancelled = false;

        while (connectionCancelled == false) {

            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            Log.d("StreamingApp", "Server says: " + message);

            setTextOfTextView(message); //!!!! This is it. At this line it 
                                        //crashes, no matter how I put it. 
                                        //Doing it directly here or in an 
                                        //extra method like now.
        }

    } catch (
            UnknownHostException e)

    {
        e.printStackTrace();
    } catch (
            IOException e)

    {
        e.printStackTrace();
    }
    return null;
}

public void setTextOfTextView(String textOfTextView) {
    TextView textView1 = executingActivity.findViewById(R.id.textView1);
    textView1.setText("Server says: " + textOfTextView);
}
}

В конце концов, я очень новичок в Java, и мои знания не выходят далеко за рамки того, что я опубликовал здесь. Прошу прощения за ошибки форматирования и не лучшие практики, я рад учиться и совершенствоваться!

Операция setText не может выполняться в фоновом потоке. Используйте ProgressUpdate в классе Async или runOnUiThread.

Luvnish Monga 21.04.2018 14:06

@PeterSagichnit Вам удалось это сделать?

Levi Moreira 21.04.2018 14:53
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

protected void onProgressUpdate(String... progress) {
         setTextOfTextView(progress[0]);
     }

Чтобы отправить обновления из doInBackground в onProgressUpdate, вы вызываете метод publishProgress()

@Override
protected Void doInBackground(Void... voids) {

    try (Socket socket = new Socket(IP, port)) {

        Boolean connectionCancelled = false;

        while (connectionCancelled == false) {

            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            Log.d("StreamingApp", "Server says: " + message);

            publishProgress(message); //!!!! This is it. At this line it 
                                        //crashes, no matter how I put it. 
                                        //Doing it directly here or in an 
                                        //extra method like now.
        }

    } catch (
            UnknownHostException e)

    {
        e.printStackTrace();
    } catch (
            IOException e)

    {
        e.printStackTrace();
    }
    return null;
}

Обновлено:

Более того, чтобы опубликовать какие-либо результаты, вы должны изменить свою подпись AsyncTask на AsyncTask<Void, String, Void>

Я скопировал вашу строку в метод и поместил бит onProgressUpdate под DoInBackground, но, похоже, это не работает. Android Studio помечает имя строки (сообщение) красным и хочет, чтобы я создал метод с именем publishProgress, в то время как метод onProgressUpdate помечен как неиспользуемый ... они почему-то не находят друг друга, я думаю?

Peter Sagichnit 21.04.2018 15:02

Но огромное спасибо за быстрый ответ! Вроде очень логично!

Peter Sagichnit 21.04.2018 15:03

Извините, я забыл упомянуть, ваша подпись Async Task сообщает Android, что вы не публикуете какой-либо прогресс AsyncTask <Void, Void, Void>, просто измените его на AsyncTask <Void, String, Void>, который, я считаю, должен работать :)

Levi Moreira 21.04.2018 15:04

Это оно! Блестяще!

Peter Sagichnit 21.04.2018 15:07

отлично :) рад, что смог вам помочь, не забудьте пометить как ответ, чтобы он тоже мог помочь другим :)

Levi Moreira 21.04.2018 15:09

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