This question already has answers here:
Android "Only the original thread that created a view hierarchy can touch its views."
(33 answers)
Closed 7 years ago.
My app crashes if TextView.setText is inside Thread:
NOTE: The following class is inside of MainActivity.
private class StreamThread extends Thread {
public StreamThread() {
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
String message = new String(buffer, 0, bytes);
//THIS IS IMPORTANT, READ THIS PLEASE
//I tested many times my app to find the problem, and I found, my app crashes when TextView.setText() is executed
//Here starts the problem
((TextView) findViewById(R.id.textView)).setText(message);
} catch (IOException e) {
break;
}
}
}
This should do the trick:
private class StreamThread extends Thread {
public StreamThread() {}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
String message = new String(buffer, 0, bytes);
runOnUiThread(new Runnable() {
#Override
public void run() {
((TextView) findViewById(R.id.textView)).setText(message);
}
});
} catch (IOException e) {
break;
}
}
}
}
Sooo, what was wrong?
Android's UI is single threaded.
This means you are not allowed to change the ui from another thread than the ui thread.
You can post changes to the ui thread using the runOnUiThread-Method or using a Handler.
Threads are designed for execute code by separated allowing another codes execute to the same time.
Unafortunately Threads are not compatible with UI, but I have a solution.
runOnUiThread(new Runnable() {
#Override
public void run () {
//Some stuff that needs to interact with the user (UI).
}
}
You must update visual components in the ui thread. For your purpose you should use an AsyncTask, Service or a Runnable which runs in the ui thread.
For example, you use an AsyncTask like in the following code:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textview = (TextView) findViewById(R.id.textView);
new StreamAsyncTask(textview).execute();
}
private class StreamAsyncTask extends AsyncTask<Void, String, Void> {
private TextView textview;
public StreamAsyncTask(TextView textview) {
this.textview = textview;
}
#Override
protected Void doInBackground(Void... voids) {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
String message = new String(buffer, 0, bytes);
publishProgress(message);
} catch (IOException e) {
break;
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
textview.setText(values[0]);
}
}
}
Or you can use the Activity's method runOnUiThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
((TextView) findViewById(R.id.textView)).setText(message);
}
});
The last way is easier to understand but the first one is more flexible.
Read about AsyncTasks: http://developer.android.com/reference/android/os/AsyncTask.html
Related
This question already has answers here:
CalledFromWrongThreadException
(2 answers)
Closed 6 years ago.
I'm trying to make a small Chat app on Android. I'm trying to send some text messages from Android to my server on PC.
I'm using some knowledge that I've found on Android developers and I created a new class for an AsyncTask:
public class ChatTask extends AsyncTask<Void, Void, Void>
{
private BufferedReader in;
private PrintWriter out;
#Override
protected Void doInBackground(Void... voids)
{
try
{
startClient();
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
// Chat start method
private void startClient() throws IOException
{
Socket socket = new Socket(ipAddress, 9001);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
writeText("Conectat la serverul: " + ipAddress);
while (true)
{
String line = in.readLine();
if (line.startsWith("SUBMITNAME"))
{
out.println(name);
}
else if (line.startsWith("NAMEACCEPTED"))
{
}
else if (line.startsWith("MESSAGE"))
{
writeText(line.substring(8) + "\n");
}
}
}
// Write to TextView
private void writeText(String text)
{
ChatActivity.setsTextToSend(text + "\n");
}
// Write to server
public void sendTextToServer(final String text)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
out.println(text + "\n");;
}
});
thread.start();
}
}
Then I linked the Task with my main Activity. I called the Task.execute() function and it's working well (i think) until the point I write a message and press send. The thing is my message that I write is sent over to the server but the my application crashes and it's giveing me this error:
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: ro.remus.messenger, PID: 3460
java.lang.RuntimeException: An error occurred while executing doInBackground()
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at ro.remus.messenger.ChatActivity.writeTo(ChatActivity.java:90)
at ro.remus.messenger.ChatActivity.setsTextToSend(ChatActivity.java:26)
at ro.remus.messenger.ChatTask.writeText(ChatTask.java:70)
at ro.remus.messenger.ChatTask.startClient(ChatTask.java:62)
at ro.remus.messenger.ChatTask.doInBackground(ChatTask.java:29)
at ro.remus.messenger.ChatTask.doInBackground(ChatTask.java:15)
[ I didn't post the entire error because is not relevant ]
My code in the main activity where the error keeps showing is:
btnSend.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
chatTask.sendTextToServer(getsTextToSend());
}
});
//textToSend.setText("");
}
});
private static void writeTo(String text)
{
tvReceivedText.append(text);
}
I know I'm doing some weird code here but it is doing (somehow) what I want until the point it crashes after I hit 'Send'.
How can I fix this error and stop it from crashing my app ?
Thanks in advance.
Anymore info that is needed I will provide.
As the error say, only the UIThread can make changes to the UI, in this case the TextView. You can use this answer to call sendTextToSend
My MainActivity has 2 views: TextView and a Button. On button click, I am running an AsyncTask which further creates 10 new AsyncTasks for network operations. Every new task creation is delayed by 1 sec. The code is:
public class MainActivity extends ActionBarActivity
{
TextView tv;
Button t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView1);
t = (Button) findViewById(R.id.toggleButton1);
t.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
getData();
}
});
}
void getData()
{
SuperNetworkAsyncTask s = new SuperNetworkAsyncTask();
s.execute("");
}
private class SuperNetworkAsyncTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
for(int i=0;i<10;i++)
{
{
nTask = new NetworkAsyncTask();
nTask.execute("");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return "";
}
#Override
protected void onPostExecute(String result)
{
}
}
private class NetworkAsyncTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
return String.valueOf(System.currentTimeMillis());
}
#Override
protected void onPostExecute(String result)
{
tv.setText(result);
}
}
}
I was expecting that the moment first NetworkAsyncTask execute method is called, it will start execution. But when I run it, I do not find any NetworkAsyncTask begin its execution until the control comes out of SuperNetworkAsyncTask. Is there any way to push the execution of NetworkAsyncTask thread as soon as execute method is called?
Some clarifications:
Why NetworkAsyncTask are created by SuperNetworkAsyncTask? Because If I create the NetworkAsyncTask in main thread, I get my UI freeze for some time.
Why making 10 object? The purpose of NetworkAsyncTask is to read data from a server at interval of 1 sec for n seconds, here n=10.
Part 2: Updates after doing some tests.
Observation 1:
As a fellow Brian shared a way to avoid creating AsyncTasks in nested way, I tried his code:
void getData() {
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
nTask = new NetworkAsyncTask();
nTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
}
This freezes my UI for few seconds and then the screen is updated in a fraction of second. It is quite surprising to me too.
Observation 2:
With java.lang.Thread, I experimented to make sure that 1) The threads should be executed right away when run() called. 2) The next task will be created only after previous task is finished.
Code:
public static void main(String[] args) {
myThread m;
for (int i=0;i<10;i++)
{
m=new myThread(String.valueOf(i));
m.start();
synchronized (m)
{
try {
m.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class myThread extends Thread
{
public String name = "";
public myThread(String n)
{
name = n;
}
public void run()
{
synchronized (this)
{
System.out.println(" Thread Name = " + name);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyAll();
}
}
}
Output:
Thread Name = 0
Thread Name = 1
Thread Name = 2
Thread Name = 3
Thread Name = 4
Thread Name = 5
Thread Name = 6
Thread Name = 7
Thread Name = 8
Thread Name = 9
Based in this, I updated my NetworkAsyncTask & SuperNetworkAsyncTask as:
private class NetworkAsyncTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
synchronized (this)
{
return String.valueOf(System.currentTimeMillis());
}
}
#Override
protected void onPostExecute(String result)
{
synchronized (this)
{
tv.setText(result);
notifyAll();
}
}
}
private class SuperNetworkAsyncTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
for(int i=0;i<10;i++)
{
nTask = new NetworkAsyncTask();
nTask.execute(url);
synchronized (nTask)
{
try {
nTask.wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return "";
}
#Override
protected void onPostExecute(String result)
{
}
}
With this code the wait() keeps on waiting indefinitely.
Finally I replaced:
nTask.execute(url);
with
nTask.executeOnExecutor(THREAD_POOL_EXECUTOR, "");
This worked well as expected.
The UI will be updated only at onPostExecute(). See notes on AsyncTask
Click here! And Try to avoid 10 AysncTasks, it does not make any sense.
You don't need to use a "super async task" use a runnable and then create new async tasks in parallel
void getData() {
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
nTask = new NetworkAsyncTask();
nTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(runnable).start();
}
Post honeycomb you can specify to run async tasks in parallel
An AsyncTask should be started in the UI thread, not on the one doInBackground runs on. You could call publishProgress after every sleep, and spawn each AsyncTask in the resulting calls to onProgressUpdate, which run on the UI thread.
I'm trying to do a simple HTTP request in Android. It has to be in separate theread. But how can I operate on the view controls inside the thread?
Here's what I have now:
public void saveData(final View v)
{
Button btn = (Button) v.findViewById(R.id.button);
btn.setText("Saving...");
new Thread() {
public void run()
{
HttpURLConnection connection = null;
try {
URL myUrl = new URL("http://example.com");
connection = (HttpURLConnection)myUrl.openConnection();
InputStream inputStream = connection.getInputStream();
final String fResponse = IOUtils.toString(inputStream);
}
catch (MalformedURLException ex) {
Log.e("aaa", "Invalid URL", ex);
}
catch (IOException ex) {
Log.e("aaa", "IO Exception", ex);
}
finally {
if (connection != null) {
connection.disconnect();
}
}
// How can I access v and btn here?
// btn.getText("Saved, thanks.");
// btn.setText("Saved, thanks.");
}
}.start();
}
To elaborate what I'm trying to achieve:
I have a text box and a button. Once the button is clicked, I want to get the text from text box, use in the URL, wich returns a value, then update the button text with this value.
Here's an example on how you could do it.
public class YourClass extends Activity {
private Button myButton;
//create an handler
private final Handler myHandler = new Handler();
final Runnable updateRunnable = new Runnable() {
public void run() {
//call the activity method that updates the UI
updateUI();
}
};
private void updateUI()
{
// ... update the UI
}
private void doSomeHardWork()
{
//update the UI using the handler and the runnable
myHandler.post(updateRunnable);
}
private OnClickListener buttonListener = new OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
doSomeHardWork();
}).start();
}
};
}
As you can see, you need to update the UI with yet another Runnable object. This is one way of doing it.
Another option is via the runOnUiThread function
runOnUiThread(new Runnable()
{
#Override
public void run() {
updateActivity();
}
});
If you try to access your Views directly from another thread like that, you will get an exception because all UI operations must be performed on the main thread.
One method that the Android SDK provides for performing background tasks that need to update the UI is the AsyncTask.
The onPostExecute() method of an AsyncTask is called after doInBackground() returns, and is run on the UI thread.
Your AsyncTask might look something like this:
public class MyBackgroundTask extends AsyncTask<URL, Void, String> {
protected String doInBackground(URL... urls) {
URL myUrl = new URL("http://example.com");
connection = (HttpURLConnection)myUrl.openConnection();
InputStream inputStream = connection.getInputStream();
return IOUtils.toString(inputStream);
}
protected void onPostExecute(String result) {
// Call back to your Activity with the result here
}
}
I use a worker thread to read text from a url. My thread is as follow. In the first time running, I am sure thread running is finished as I can check sdcard_readstr is null.
In the second time running, when I call thread_download.start();, then the program crashed.
What could be wrong? Thanks
public class DownloadingThread extends AbstractDataDownloading {
#Override
public void doRun() {
// TODO Auto-generated method stub
try {
// Create a URL for the desired page
URL url = new URL(SDcard_DetailView.textfileurl);
// Read all the text returned by the server
BufferedReader in = new BufferedReader(new
InputStreamReader(url.openStream()));
do{
sdcard_readstr = in.readLine();
}while(sdcard_readstr!=null);
in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
}
}
public abstract class AbstractDataDownloading extends Thread{
private final Set<ThreadCompleteListener> listeners
= new CopyOnWriteArraySet<ThreadCompleteListener>();
public final void addListener(final ThreadCompleteListener listener) {
listeners.add(listener);
}
public final void removeListener(final ThreadCompleteListener listener) {
listeners.remove(listener);
}
private final void notifyListeners() {
for (ThreadCompleteListener listener : listeners) {
listener.notifyOfThreadComplete(this);
}
}
#Override
public final void run() {
try {
doRun();
} finally {
notifyListeners();
}
}
public abstract void doRun();
}
EDIT1:
In my thread complete notification, I use runOnUiThreadto use the UI components.
Is that causing problem?
public void notifyOfThreadComplete(Thread thread) {
// TODO Auto-generated method stub
if(downloadingStopbuttonispressed == false){//background process completed
textfileurl = null;
this.runOnUiThread(new Runnable() {
public void run() {
Wifibutton = (Button) findViewById(R.id.Wifiscanning);
Wifibutton.setText("Load another day's data");
final MenuItem refreshItem = optionsMenu.findItem(R.id.airport_menuRefresh);
refreshItem.setActionView(null);
}
});
}
}
I called thread start in onResume() as
#Override
protected void onResume() {
super.onResume();
if(textfileurl != null){
Wifibutton.setText("Stop Data Loading");
buttonStatus = "loading";
setRefreshActionButtonState(true);
thread_download.start();
}
}
EDIT2:
My LogCat image is attached.
My solution is here . I can't reuse the same instance of the Thread object in the second time. I need to create a new instance to call the Thread in the second time. So Thread is suitable for single time running process, for multiple time running process I should use AsyncTask. Even AsyncTack is only for one time execution and for multiple time execution, we should use as new MyAsyncTask().execute(""); I don't understand why people downvote with no reason given. I couldn't find the link in my first search.
I am having problems with updating the TextView, I used the Handler method to pass the message to the UI. My application receives data(type integers) true io stream and shows in TextView.
My Activity class looks like this:
public class DeviceView extends Activity {
TextView dataX;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.device_view);
dataX = (TextView) findViewById(R.id.datax);
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
dataX.setText(String.valueOf(msg.arg1));
}
};
}
}
I also have a separate class it extends Thread:
public class IOThread extends Thread {
public void run() {
byte[] buffer = new byte[1024];
int data;
while (true) {
try {
data = in.read(buffer);
Message message= Message.obtain();
message.arg1= data;
DeviceView.handler.sendMessage(message);
} catch (IOException ex) {
break;
}
}
}
}
Do I have to make a separate variable type String and point it to variable data and at last calling the count? Would that be enough to update TextView?
Can you try using an interface. Let the Activity implement it, pass it to the IOThread class. Once you get the result, pass the result to the Activity.
Interface named InterfaceData
public void getData(int data);
public class DeviceView extends Activity implements InterfaceData{
TextView dataX;
Handler handler;
IOThread ioThread;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.device_view);
handler = new Handler();
ioThread = new IOThread(this);
dataX = (TextView) findViewById(R.id.datax);
}
#Override
public void getData(int data){
handler.postDelayed(new Runnable(){
public void run(){
dataX.setText(data);
};
},100);
}
}
> Thread class
public class IOThread extends Thread {
InterfaceData interfaceData;
public IOThread(InterfaceData interfaceData){
this.interfaceData = interfaceData;
}
public void run() {
byte[] buffer = new byte[1024];
int data;
while (true) {
try {
data = in.read(buffer);
interfaceData.getData(data);
} catch (IOException ex) {
break;
}
}
}
}
I have found my problem it was not the Handler issue. THe code i posted at the beginning is coorect. The problem lyis on the way i read the received bytes[] array from the InputStream. I have tested by sending an integer int numbers = (int) 2 and when print this receivd data in terminal in Android app, it receivs only 1, even if i send int 3 or 4, i stil receive 1.
So i preceiated your example code #dcanh121 , but my question is actualy how do i read properly the integers that the server sends?
public void run() {
byte[] buffer = new byte[1024];
int data;
while (true) {
try {
data = in.read(buffer);
Log.d(TAG + data, "test");
Message message = Message.obtain();
message.arg1 = data;
Log.d(TAG + message.arg1, "test");
DeviceView.handler.sendMessageDelayed(message, 100);
} catch (IOException ex) {
Log.e(TAG_IOThread, "disconnected", ex);
break;
}
}
}