Issue
I am an university student programming an android app that shall display messages, which the android device receives via TCP from a server.
The class ListenForMessage (extending AsyncTask) opens the TCP connection and receives messages from the server within the DoInBackground method. I can print out the messages from the server in the log, but if I try to edit the text of a TextView, my app crashes. It seems I cannot edit TextView´s texts from the DoInBackground method, especially not, because the class does not extend the activity, in which the TextView is created.
I have two activities and I am executing the class "ListenForMessage" in the onCreate of the second activity "StreamPageMain" (Before that, I executed it from within MainActivity, but that did not work either). I tried to let the method know, in which activity the TextView belongs, as you can see in my code, and I created another method within the class "ListenForMessage" to set the text, so that it is not the DoInBackground method trying to change the text, but none of that worked.
Summary: I have marked the spot in the code, that goes wrong. What I need to do, is somehow taking the String called "message" and put that String in the TextView with ID "textView1" which is part of the "StreamPageMain" activity and not the MainActivity.
I start with this method in the MainActivity when a button is pressed:
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);
}
The second Activity "StreamPageMain" is launched. That works.
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();
}
}
You can see, how I am passing the Activity StreamPageMain to the ListenForMessage class here. The class itself, the connection and everything works fine if I comment out the marked line:
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);
}
}
I am after all very new to Java and my knowledge does not go far beyond what I have posted here. Please excuse formatting mistakes and not-best practices, I am happy to learn and improve!
Solution
As you saw you can't make changes to the UI in doInBackground
because the code in there is executed in another thread and you can only make changes to the UI in the UI thread. (You can post changes to the UI thread from a nother thread though).
To update the UI in AsyncTasks
you can use the onProgressUpdate
to update the UI because the code here runs on the UI thread. You override this method in the AsyncTask
:
protected void onProgressUpdate(String... progress) {
setTextOfTextView(progress[0]);
}
To send the updates from doInBackground
to the onProgressUpdate you call the method 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;
}
EDIT:
Moreover, in order to publish any results you should change your AsyncTask signature to AsyncTask<Void, String, Void>
Answered By - Levi Moreira
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.