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
Related
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.
I created an application which uses a socket connection to my server. This server sends an information string to the phone which reads this with a BufferedReader.
Now i want to display this information on the screen(in a textbox for example) but the textbox.append command doesn't work in this case.
There is no error but at runtime it won't add the string to the textbox.
Tried the same with textviews. Here the part of this code.
The commands() function is called in the connect progress and the variables are declared at the beginning.
public void commands() throws Exception{
Responce = buffer.readLine();
final TextView textViewToChange = (TextView) findViewById(R.id.textView1);
textViewToChange.setText(Responce);
commands(); }
Would be nice if anyone know how to fix this problem.
Thanks :)
You need to update UI on the main UI Thread. You can use runOnUiThread as below.
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
textViewToChange.setText(Responce);
}
});
UI in android is not updateble from another Thread. Look at
AsyncTask
Read this also Ui Update Android
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();
One of my activities make a http request to a webservice to get some weather data when I start the application.
The issue that the activity will take 3-4 seconds to display because of the webservice request. ( Tested on actual device )
I know I m not doing this the right way. All I m doing is on the onCreate method, I m making the request , getting the xml back, parsing and displaying the data.
What is the best way to deal with webservice requests in Android so the application won't display a white screen while the request is being made? Maybe some threads.......
I know this is not happening on other application I have in my device that make request to get live data.
Notes:
1) The xml I getting back is not that big ( 5 elements with 5 nested elements on each one).
2) I tried with the 3G network and Wifi but the response time is still the same.
sample code:
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.clock_weather);
// this is where it is making the request and parsing the xml.
WeatherSet set = getWeatherCondition("New York, NY");
TextView currentWeather = (TextView) findViewById(R.id.current_weather);
currentWeather.setText("" + set.getWeatherCurrentCondition().getTempFahrenheit());
TextView currentWeatherH = (TextView) findViewById(R.id.current_weatherH);
currentWeatherH.setText("H: " + set.getWeatherForecastConditions().get(0).getTempMaxFahrenheit());
TextView currentWeatherL = (TextView) findViewById(R.id.current_weatherL);
currentWeatherL.setText("L: " + set.getWeatherForecastConditions().get(0).getTempMinFahrenheit());
ImageView currentWeatherIcon = (ImageView) findViewById(R.id.current_weather_icon);
String imageUrl = set.getWeatherCurrentCondition().getIconURL();
Drawable bitmapDrawable = getImageBitmap(imageUrl);
currentWeatherIcon.setImageDrawable(bitmapDrawable);
setForecastInfo(set, R.id.day1, R.id.day1_icon, R.id.day1_temp, 1 );
setForecastInfo(set, R.id.day2, R.id.day2_icon, R.id.day2_temp, 2 );
setForecastInfo(set, R.id.day3, R.id.day3_icon, R.id.day3_temp, 3 );
setForecastInfo(set, R.id.day4, R.id.day4_icon, R.id.day4_temp, 4 );
}
The time for your response is unpredictable - your network connection can be very poor and take seconds to transfer a few bytes. So the correct way to do this ( as you propose ) is to use thread. In our case android provides very useful class to handle this situations - AsynTask. After you read the docs you will notice that it has 3 very powerful methods that can help you
onPreExecute runs in the ui thread - very helpful to show some spinner or some progress indicator to show the user that you are doing some work in background
doInBackground runs in background - do your background work here
onPostExecute runs in the ui thread- when your are done with your background work hide the progress and update the gui with the newly received data.
private class getWeather extends AsyncTask<Context, Void, Cursor> {
ProgressDialog dialog = null;
protected void onPreExecute () {
dialog = ProgressDialog.show(CLASS.this, "",
"Loading. Please wait...", true);
}
#Override
protected Cursor doInBackground(Context... params) {
WeatherSet set = getWeatherCondition("New York, NY");
return null;
}
protected void onPostExecute(Cursor c) {
dialog.dismiss();
}
}
Then where you have WeatherSet set = getWeatherCondition("New York, NY"); now, you'll put new getWeather().execute(this);
I suggest reading how the AsyncTask works, and see why this should work. It goes outside the onCreate() method.
This is regarding AsyncTask, I just want to help understanding the concept, it is really useful:
DownloadFilesTask dft = new DownloadFilesTask(this);
//Executes the task with the specified parameters
dft.execute(Void1...);
...
...
...
dft.cancel(boolean);
private class DownloadFilesTask extends AsyncTask<Void1, Void2, Void3> {
//Runs on the UI thread before doInBackground(Void1...)
protected void onPreExecute() {
}
//runs in BACKGROUNG threat
protected Void3 doInBackground(Void1... urls) {
//it can be invoked from doInBackground(Void1...) to publish updates
//on the UI thread while doInBackground(Void1...) is still running
publishProgress(Void2...);
}
//Runs on the UI thread after publishProgress(Void2...) is invoked
protected void onProgressUpdate(Void2... progress) {
}
//Runs on the UI thread after doInBackground(Void1...) has finished
protected void onPostExecute(Void3) {
}
//runs in UI threat after cancel(boolean) is invoked and
//doInBackground(Void1...) has finished
protected void onCancelled(Void3) {
}
}
You can use AsynchTask class for your web service.You can write your time consuming task in on doInBackground.Also you can use a progress Dialog.
Here You can see how to work with AsynchTask.You can also update your UI while web service is parsing without waiting for the complete parsing using onPostUpdate method.
The response time is normal. Don't worry. Make it a point to run the web-service call in a separate thread.
Regarding the white screen, as soon as you start the web service call, fire a ProgressDialog box. This will run till you receive the response. As soon as you receive the response, dismiss the progressDialog box and start the new activity where you can display the result.
Use the following URLs for reference
http://www.helloandroid.com/tutorials/using-threads-and-progressdialog
http://thedevelopersinfo.wordpress.com/2009/10/16/showing-progressdialog-in-android-activity/
I have implemented the idea I'm giving you and it works perfectly.
Hope I was of some help
What is the best way to deal with webservice requests in Android so the application won't display a white screen while the request is being made?
Because you said 'white screen" I am assuming you are not using a progress dialog. You need to show a progress spinner/dialog to let the user know you are processing.
Have you check how large the data is? If the data is too large you really cant do anything , if you have control over the service its best to reduce the size.
We have a ViewerFilter for a TableViewer that is a little slow, so to try to give the impression of awesomeness, we wanted to have the viewer wait 500 milliseconds before refreshing the window (otherwise, it was blocking after every key stroke).
Not having any clue what I was doing, I tried creating a class that would check if System.currentTimeMillis() was greater then the time of the last key stroke + 500 from a different thread. This just caused an Invalid thread access exception to be thrown, so I'm lost.
Edit: I was able to use TableViewer.getTable().getDisplay().asyncExec() to sidestep the invalid thread problem, but I don't like my solution, and would love to hear other suggestions.
You might want to try to turn off redraw while updating the viewer.
Viewer.getControl().setRedraw(false);
// update
Viewer.getControl().setRedraw(true);
It can sometimes give a better user experience. You can also schedule a ui-job that you cancel when the user hits a new key or modifies the text. E.g.
class RefreshJob extends WorkbenchJob
{
public RefreshJob()
{
super("Refresh Job");
setSystem(true); // set to false to show progress to user
}
public IStatus runInUIThread(IProgressMonitor monitor)
{
monitor.beginTask("Refreshing", ProgressMonitor.UNKNOWN);
m_viewer.refresh();
monitor.done();
return Status.OK_STATUS;
};
}
and then reschedule the refresh in a separate job.
private RefreshJob m_refreshJob = new RefreshJob();
private Text m_filterText;
void hookModifyListener()
{
m_filterText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
m_refreshJob.cancel();
m_refreshJob.schedule(500);
}
});
}
If the user hits the Enter key you can schedule a refresh job without the delay,
Just wrap your code in display.syncExec, something like this:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// check refresh time
// refresh.
}
});
You may want to look in to asyncExec too, if syncExec does not meet your needs.