I have a thread which updates a textview using a runnable:
// runnable to allow updating the UI from the thread
Runnable updateTextView = new Runnable() {
public void run() {
mTextView.setText(mDisplayedText);
mTextView.invalidate();
}
};
However, the text does not update properly. It works for the first few writes, then only writes half the text, leaving the second half of the previous text there.
Turning the screen around causes it to refresh and draws it correctly.
The textview is multiline and I write a string to it which contains \n characters for the end of lines.
The invalidate call above makes no difference.
Any ideas?
UPDATE: mDisplayedText is declared in the activity which also contains my thread class. in the thread run loop, I call:
mDisplayedText = getText()
runOnUiThread(updateTextView);
The loop contains a 100ms sleep, but it only writes to the text when it has changed so in reality it will be less than once a second
ANSWER:
Slightly embarrassing this. The problem was in other code.
I was receiving a UDP socket into the same packet, and reading the packet.getData into a new string.
This was copying the whole packet into the string, rather than just the bytes received in that message.
The second problem was that I needed to call packet.setLength each time to set the length available to the whole packet.
Thanks for the answers!
you can only update your UI on UI thread, put your setText() method inside this
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText(someValue);
}
});
Runnable is an interface. Think of it like a set of commands ready for execution. You can use a Handler to work with your code:
Handler textViewHandler = new Handler();
// runnable to allow updating the UI from the thread
Runnable updateTextView = new Runnable() {
public void run() {
mTextView.setText(mDisplayedText);
}
};
textViewHandler.post(updateTextView);
Also, please consider #Karakuri's suggestion.
Related
Good day and I am here to take some advice from you. Basically I have a class that has two methods as follows. It allows me to send data to the serial port (UART) and I receive data from it. Works fine.
Now, I have to modify it in such a way that it will send some data to the serial port serialPort.writeBytes(src.getBytes()); and wait for the reply for 3 seconds, if I get a reply in between 3 seconds it has to be updated to a TextView and if not it has to send the second data (serialPort.writeBytes(src1.getBytes());)and again wait for the reply.
I am not an expert in Threads and Handlers as I have just started to learn it. Please advise me on this. Thank you very much for your time.
public void sendData(){
final String src = "MYDATA\r";
final String src1 = "MYDATA1\r";
serialPort.writeBytes(src.getBytes());
**// Pause here and wait for the reply**
serialPort.writeBytes(src1.getBytes());
**// Pause here and wait for reply**
}
private class ReadThread extends Thread {
byte[] buf = new byte[512];
#Override
public void run() {
super.run();
while (!isInterrupted()) {
int size;
if (mInputStream == null)
return;
size = serialPort.readBytes(buf);
if (size > 0) {
byte[] dest = new byte[size];
System.arraycopy(buf, 0, dest, 0, size);
byteLinkedList.offer(dest);
onDataReceived();
}
}
}
}
Now
This is not an answer, I cant comment cause I don't have enough rep.
I agree with momo. So you will have something like this:
class SendThread extends Thread{
boolean gotData = false; //flag to check if data is received.
#Override
public void run(){
while(gotData == false){
new Runnable() {
#Override
public void run(){
//SendData code
//if you get Data change gotData = true;
}
}.run();
Thread.sleep(3000); //sleep for 3 secs
}
}
}
If you don't have a complex situation, you can use the Android AsyncTask - do a search and you will find a lot of references to it.
Essentially, you need an interface that you can call to respond when the data is received. Polling is a matter of calling wait on this thread. If you are not familiar with wait, you can also search easily for it.
The key issue is that the UI should not wait and with a thread or AsyncTask it will not.
If you decide to use threads, then look into creating a simple "listener" (aka interface). It might seem intimidating, but basically it is an empty class that you will need to pass into your custom thread.
Also, you may need to consider if your Activity will still be visible during this process. If it might not be, and you still need to complete the task in the background, you will need a Service
This is a good "overview" of your options: http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html
Since you really haven't defined the full use-case, you probably need to do more research or be more specific. But this should get you going in the right direction.
I am programming an app in Android, where I obtain the data from the microphone, it works fine, I am able to save the amplitude of the sound. And I can update the progresssbar.
barraThread = new Thread(new Runnable() {
public void run(){
while (isRecording){
actualizarBarra(getAmplitud());
//lector.setText(String.valueOf(getAmplitud()));
}
}
},"barra Thread");
-Where: actualizarBarra(getAmplitud()); update the value of the progressbar with the actual amplitude.
My problems is that if I try to update a textView from that point, I get the error (
“Only the original thread that created a view hierarchy can touch its
views.”
So I tried to update the textView using a "Handler" and also using "runOnUiThread(new Runnable() {..." but the app runs very slow and sometime crash. Do you know any way to update the progressbar and the textview at the same time, in a properly way?,
Thanks a lot
Using the the runOnUiThread solution.
private void actualizarInterfaz() {
runOnUiThread(new Runnable() {
#Override
public void run() {
actualizarBarra(getAmplitud());
lector.setText(String.valueOf(getAmplitud()));
}
});
}
and then in a loop:
while(isRecording){
actualizarInterfaz();
}
Was no error, just the app get frozen, so I can not see any log at the LogCat, just like is running normally but in fact does not.
Thanks
You should update TextView on the UI (main) thread only. The handler approach is ok (there are others). As for the performance issue, I suspect the recording make too many frequent updates. Try to introduce delay in updating the view and not update it every time
I've created a Handler that can be accessed from anywhere within the activity and also written a method to make it easier to call the handler:
private Handler textFromBGThread = new Handler() {
#Override
public void handleMessage (Message msg) {
// Get the string from the msg
String outputString = msg.getData().getString("Output");
// Find the TextView
TextView Output = (TextView)findViewById(R.id.ConsoleOutputView);
// Display the output
Log.i("TextOutput","About to display message: " + outputString);
Output.setText(Output.getText() + outputString);
Log.i("TextOutput","Message displayed");
}
};
private void TextOutputWrapper (String outputText) {
Message msg = new Message();
Bundle bndle = new Bundle();
bndle.putString("Output", "\n" + outputText);
msg.setData(bndle);
textFromBGThread.handleMessage(msg);
}
So then this can be called from a background thread simply with:
TextOutputWrapper("Attemping to connect...");
This will work 1+ times, however, the actual visual change will cause a CalledFromWrongThreadException to be thrown. Being new to Java & Android, I'm stuck on why this is happening.
I have noticed that the crash tends to happen when there's a slightly longer time period between calls & that if the calls to TextOutputWrapper(String) are happening very quickly after one another, then it works. For example, this:
int i = 0;
while (i < 200) {
TextOutputWrapper(String.valueOf(i));
i++;
}
works fine.
Having looked at LogCat, it seems that the garbage collector frees up some resources and then the next time TextOutputWrapper(String) is called, it crashes (when Output.SetText(String) is called, to be precise), although I'm not exactly sure why that would cause this error.
There's a few things I'd change here:
Using Handler
A Handler is useful if you want to trigger the UI to update, and do so from a non-UI thread (aka "background" thread).
In your case, it's not serving that purpose. You are directly calling
textFromBGThread.handleMessage(msg);
It's not designed for you to do that. The way you are supposed to use Handler is to implement what you want done to the UI in the handleMessage(Message) method. You did that. But, you shouldn't directly call handleMessage(). If you do that, then handleMessage() will be called from whatever thread invokes TextOutputWrapper(). If that's a background thread, then that's wrong.
What you want to do is to call the handler's sendMessage(Message) method (or one of the other available variants). sendMessage() will put your message in a thread-safe queue, that is then processed on the main thread. The main thread will then invoke your handler's handleMessage(), passing it back the queued message, and allowing it to safely change the UI. So, change TextOutputWrapper() to use this:
private void TextOutputWrapper (String outputText) {
Message msg = new Message();
Bundle bndle = new Bundle();
bndle.putString("Output", "\n" + outputText);
msg.setData(bndle);
textFromBGThread.sendMessage(msg);
}
Java Conventions
This code is a bit hard to read, for an experienced Java developer. In Java, typical coding standards reserve upper case names for things like classes, while methods start with lower case letters. So, please rename the method to:
private void textOutputWrapper (String outputText);
or, better yet, since this is in fact a method, and not a wrapper, per se, rename to something like
private void outputText(String text);
Safe Threading Alternatives
Finally, I might recommend that if you simply want a method that allows you to safely modify the UI from any thread, use another technique. I don't find Handler to be that easy to use for beginners.
private void outputText(final String outputString) {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Find the TextView
TextView output = (TextView)findViewById(R.id.ConsoleOutputView);
// Display the output
Log.i("TextOutput","About to display message: " + outputString);
output.setText(Output.getText() + outputString);
Log.i("TextOutput","Message displayed");
}
});
}
runOnUiThread() is a method available in every Activity.
I'll also point you to some general docs on understanding threading in Android:
http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html
http://android-developers.blogspot.com/2009/05/painless-threading.html
I have a bunch of Buttons (custom, with a few extra methods to apply other backgrounds, and revert to original), which can be assigned a background. But since I don't know how large these backgrounds will or can be, I decided to set the background in a separate Thread. First, I had this code:
public void updateButton(final Button b, final String d, final String s) {
b.nullify(); //Recycles previous background, else OOM error
b.setText(s);
if (d != null) {
new Thread() {
#Override
public void run() {
b.setBackgroundFromBase64(d);
}
}.run();
}
else b.setBackgroundToDefault(); //Sets standard android background
}
but I soon found out that this wasn't the way to go. Slowly, the memory got flooded when I called this method a few thousand times. When I removed the new Thread() part, the memory wasn't flooded, so the Thread was the cause of this (Memory Analyzer Tool told me the same).
Then I tried the AsyncTask:
private class UpdateButtonTask extends AsyncTask<Object, Void, Void> {
#Override
protected Void doInBackground(Object... objs) {
String s = (String)objs[0];
Button b = (Button)objs[1];
String d = (String) objs[2];
b.nullify(); //Recycles previous background, else OOM error
b.setText(s);
if (d != null) b.setBackgroundFromBase64(d);
else b.setBackgroundToDefault();
return null;
}
#Override
protected void onProgressUpdate(Void v) {
//As far as I know, I don't need this method
}
#Override
protected void onPostExecute(Void v) {
//Neither this one
}
}
and call
new UpdateButtonTask().execute("Button", myButton, base64String);
in the button update method. But that didn't work either (the button wasn't updated at all. No text, no custom background). I read some articles and questions about Threading (this, this, and this amongst others), but I can't seem to get it working. For now, no Thread and hoping the phones are fast enough to not cause trouble on the UI thread seems to be the best option, but I'd rather have something more reliable, like a Thread.
What am I doing wrong? or maybe just the full question How can I update a Button background on a background Thread (so the UI doesn't stop working when applying larger backgrounds)?
I'm guessing that the problem is that you're trying to update the UI on a non-UI thread. This throws an exception and kills your AsyncTask.
You should separate the processing-intensive tasks to the AsyncTask and then apply UI changes on the main thread (using runOnUiThread(Runnable)).
Since you're using a custom button, I don't really know what you're not allowed to do on a non-UI thread. I'm guessing the setText function is causing problems, if you haven't overridden it.
You should start by wrapping the whole body of your doInBackground method in a try/catch block and logging the exception to isolate the problem.
May be this code will help you
new Thread(new Runnable() {
public void run() {
context.runOnUiThread(new Runnable() {
public void run() {
b.setBackgroundFromBase64(d);
}
});
}
}).start();
I am having a problem with modifying EditText in another function started by the thread:
Thread thRead = new Thread( new Runnable(){
public void run(){
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
});
my function is as follows:
public void startReading(EditText _txtArea){
_txtArea.setText("Changed");
}
It always force closes while trying to modify the edittext. Does someone know why?
UI views should not be modified from non-UI thread. The only thread that can touch UI views is the "main" or "UI" thread, the one that calls onCreate(), onStop() and other similar component lifecycle function.
So, whenever your application tries to modify UI Views from non-UI thread, Android throws an early exception to warn you that this is not allowed. That's because UI is not thread-safe, and such an early warning is actually a great feature.
UPDATE:
You can use Activity.runOnUiThread() to update UI. Or use AsyncTask. But since in your case you need to continuously read data from Bluetooth, AsyncTask should not be used.
Here is an example for runOnUiThread():
runOnUiThread(new Runnable() {
#Override
public void run() {
//this will run on UI thread, so its safe to modify UI views.
_txtArea.setText("Changed");
}
});
First of all take a look at your log, it usually contains a stack trace when an app shuts down.
You shouldn't run the thread like you normally do, instead use runOnUiThread:
Runnable thRead = new Runnable(){
public void run() {
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
};
runOnUiThread(thRead);
The explaination: Only the UI thread is allowed to change the state of UI components.
This article may help you.
http://android-developers.blogspot.com/2009/05/painless-threading.html
There are few options:
1. run it on UI thread Activity.runOnUiThread(Runnable)
2. use AsyncTask
Except runOnUiThread() (which works), there is also another way, which I know of:
Define a handler in your UI (Activity) class:
public class MainActivity extends Activity {
.....
Handler uiThreadHandler = new Handler() {
public void handleMessage(Message msg) {
Object o = msg.obj;
if (o==null) o = "";
TextView textIn = (TextView)findViewById(R.id.textin);
textIn.setText(o.toString());
}
};
}
and from inside some thread you can call it:
Message msg = uiThreadHandler.obtainMessage();
msg.obj = "Text for EditView";
uiThreadHandler.sendMessage(msg);
By default, the main thread is the UI thread. All code that modifies the appearance of the application needs to be run in this thread. If you want to have multiple threads in your application that can modify the UI I would suggest using the AsyncTask class.
public someMethod(){
new ChangeTextTask().execute();
}
private class ChangeTextTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
startReading(_txtArea);
return null;
}
}
However, you need to take steps to prevent multiple threads from accessing the EditText object at once. Otherwise you'll wind up getting a CurrentModificationException error.
http://developer.android.com/reference/android/os/AsyncTask.html