Я студент университета, разрабатывающий приложение для 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, и мои знания не выходят далеко за рамки того, что я опубликовал здесь. Прошу прощения за ошибки форматирования и не лучшие практики, я рад учиться и совершенствоваться!
@PeterSagichnit Вам удалось это сделать?




Как вы видели, вы не можете вносить изменения в пользовательский интерфейс в 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 помечен как неиспользуемый ... они почему-то не находят друг друга, я думаю?
Но огромное спасибо за быстрый ответ! Вроде очень логично!
Извините, я забыл упомянуть, ваша подпись Async Task сообщает Android, что вы не публикуете какой-либо прогресс AsyncTask <Void, Void, Void>, просто измените его на AsyncTask <Void, String, Void>, который, я считаю, должен работать :)
Это оно! Блестяще!
отлично :) рад, что смог вам помочь, не забудьте пометить как ответ, чтобы он тоже мог помочь другим :)
Операция setText не может выполняться в фоновом потоке. Используйте ProgressUpdate в классе Async или runOnUiThread.