I am trying to process some files and it takes some time, so I would like to use a progressBar while the operation is being performed. In my activity, I have a button which starts the processing, when I press on it, the progressBar does not update and after a while the operation is complete and I see the result of the processing (so the function is properly called and act as expected).
The progressBar is visible, it just does not update. I have displayed getProgress() in the log and the value increases, only the actual progressBar does not update.
The function which should update the progressBar:
private byte[] processFiles() throws ExecutionException, InterruptedException {
//Init things
final ArrayList<String> fileNames = getFileList();
progressBar.setVisibility(View.VISIBLE);
progressBar.setMax(fileNames.size());
progressBar.setIndeterminate(false);
for (int i = 0 ; i<fileNames.size(); i++) {
//Do stuff on files
progressBar.setProgress(i+1);
Log.d("CurrentActivity", String.valueOf(progressBar.getProgress()));
}
return bos.toByteArray();
}
In the onCreate():
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setIndeterminate(false);
I have also tried as displayed in the android developer tutorial using a Thread but with no success (though I did not use a while loop but a for loop, but figured it shouldn't matter).
Can anyone help me figure out the problem?
Note I have also tried to use this before each update as suggested in some other answers:
progressBar.setProgress(0);
progressBar.setMax(fileNames.size());
Edit, here is my onClickListener:
fileButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
// Init things
try{
new Thread(new Runnable() {
public void run() {
try {
outStream = new FileOutputStream(path);
outStream.write(processFiles());
outStream.close();
runOnUiThread(new Runnable() {
#Override
public void run() {
//stuff that updates ui
}
});
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}catch(Exception e){
e.printStackTrace();
}
}
});
Now the progressBar works but using processedFile fails, it is an image that I display with Glide, it worked well before but now it doesn't display anything.
Edit 2: using runOnUiThread for all UI update worked like a charm and fixed my new issue.
You need to use two threads:
Main Thread (aka UI Thread) (aka WhereYouWorkAlways Thread)
Find and store the ProgressBar, create a Handler and then create the new Thread (see below)
Work Thread (aka just a new thread)
Inside the Thread Runnable, after each step of the work (= after each file operation is done) post a Runnable at the handler which updates the progress bar
handler.post(new Runnable() {
public void run() {
progressBar.setProgress(i);
}
})
Why two Threads are needed
The main thread is actually the UI Thread which means that every operations is placed in the "Update" step of the following cycle:
-> Update -> (Re)Draw -> Wait ->
Which means that if the Update lasts 10s then the screen will update after 10s. (Most UIs are single thread based)
Because on each iteration of cycle you have progressBar.setProgress(0);
Related
I am trying to review the concept of Handler. So I simply create a Thread to update a ProgressBar and see if it would throw an exception.
Here's my code
#Override
public void onResume() {
super.onResume();
new Thread(){
#Override
public void run() {
super.run();
while (progressBar.getProgress() <100){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setProgress(pb.getProgress()+10);
}
}
}.start();
}
My question is simple. Why it doesn't throw any CalledFromWrongThreadException?
If it meant to be this way, should we forget about Handler when we deal with progress updating?
https://cs.android.com/android/platform/superproject/+/android-10.0.0_r30:frameworks/base/core/java/android/widget/ProgressBar.java;l=1610
Bad luck--ProgressBar has special support for updating its progress from another thread. That's why it doesn't throw something.
But because it's so specifically this operation that has this extra support, no to the second question. You wouldn't, in general, stop using a handler. For cases where, for example, you need to do one other little UI thing while updating the progress.
I finally got my app working, i just have one issue which i would like to correct.
I have a button which controls a thread that runs a couple function in the background. The functions in the background eventually stop the thread whenever a certain value is reached. What i am having issues doing is pressing that same button again to just stop the thread manually. Currently I can only start the thread and wait for itself to finish. I am able to do other things in the app, so the thread is running on its own, i just want to kill it manually.
public void onMonitorClick(final View view){
if (isBLEEnabled()) {
if (!isDeviceConnected()) {
// do nothing
} else if (monitorvis == 0) {
showMonitor();
DebugLogger.v(TAG, "show monitor");
//monitorStop = 4;
Kill.runThread(); // I want a function here that would kill the
// thread below, or is there something that
// can be modified in runThread()?
// I did try Thread.Iteruppted() without luck
shutdownExecutor();
} else if (monitorvis == 1) {
hideMonitor();
DebugLogger.v(TAG, "hide monitor");
monitorStop = 0;
runThread(); //The running thread that works great on its own
}
}
else {
showBLEDialog();
}
}
private void runThread() {
new Thread() {
int i;
public void run() {
while (monitorStop != 3) { //This is where the thread stops itself
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
((ProximityService.ProximityBinder) getService()).getRssi();
rssilevel = ((ProximityService.ProximityBinder) getService()).getRssiValue();
mRSSI.setText(String.valueOf(rssilevel) + "dB");
detectRange(rssilevel);
}
});
Thread.sleep(750);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
On first look, you could simply set monitorStop = 3, which would cause the thread to eventually stop after it's timeout completes.
The problem with this, is that I presume if you push the button again or your code modifies monitorStop at some point in the future, then the thead you wanted dead, might stay alive. ie: monitorStop will need to stay equal to three for at least 750ms to assure the thread will comlete it's loop and die.
The correct way to do this would be to create your thread as a new class with it's own monitorStop parameter. When you create the thread, you would keep a reference to it and modify the thread's monitorStop parameter. This way the thread would finish without interruption. If you wanted to create a new thread, then this would not affect the old thread from finishing appropriately.
This question already has an answer here:
Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread
(1 answer)
Closed 9 years ago.
Good day,
I want to update an image button in my UI from another thread. below is my code that i run in my mains threads onCreate() method.
new Thread(new Runnable() {
public void run() {
ImageButton btn = (ImageButton) findViewById(R.id.connected_icon);
if (netConnection.IsConnected()) {
// Change icon to green
btn.setImageResource(R.drawable.green_small);
} else {
// Change icon to red
btn.setImageResource(R.drawable.red_small);
}
try {
// Sleep for a second before re_checking.
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
No when i run this i gen an error int he LogCat saying i cannot update the UI from annother thread.
I remember reading soem where once that this is the case so that you don't get multiple threads updating the same UI object at once. But how can i achieve this. i am sure there is a work around?
Thanks
You cannot directly acces UI components from the thread.
The correct way to do this is by creating a handler
final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
And send messages to UIThread with
Message msg = new Message();
//TODO: add stuff to message
mHandler.sendMessage(msg);
inside your Thread.
This or use an AsyncTask instead and do the updates from inside of pre, post or progressUpdate methods
UI Elements should be updated only from the UI thread. Use an async task to do background word, and modify the UI in onPostExecute, which runs on the UI thread
I have an app that runs 2 threads in loops. 1st one is updating a graph in 1s interval and the second one is updating another graph at 60s interval. The second task is taking a long time since it is quering some server in the internet 3 times that might not always be available and even if it is it will take up to 5-7s to execute.
What is happening is when I launch the second thread it will pause execution of the first one and that is not what I want, I wish both run concurrently. Here in the Youtube video you can see the results of the app running. http://youtu.be/l7K5zSWzlxI
"thread_updater1s" is running a green graph, large readout, and a timer in the corner so you clearly see it stalls for 11 seconds.
1)First of all why is that happening? how to fix it?
2)I'm aware that I might not launch the threads properly at all. I had hard time understanding how to make something to run in a interval loop in Java and my code worked fine for one graph/tread. Now when I have 2 loops in separate threads I don't know why they are not executing concurrently.
Here is the code:
public class LoopExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
thread_updater1s.start();
thread_updater2.start();
}// end of onCreate
final Runnable r1s = new Runnable() {
public void run() {
do_1s_updates(); // those are very quick http calls to the local API server
} // to get data nessessary for some plot.
// They have 1s timeout as well but rarely timeout
};
final Runnable r2 = new Runnable() {
public void run() {
do_large_updates(); //This makes 7 long call over the Internet to the slow https
//server once every 60s. Has 10s timeout and sometimes takes as much as
//7s to execute
}
};
Thread thread_updater1s = new Thread() {
#Override
public void run() {
try {
while (true) {
handler.post(r1s);
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread_updater2 = new Thread() {
#Override
public void run() {
try {
while (true) {
handler2.post(r2);
sleep(60000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
PS. please be forgiving and informative I only code Java for 15 days so far with absolutely no prior experince or lesson.
You need to make the http requests in the threads (not the posted runnables). Then, when you have the data downloaded, you create a runnable with that data that will update the graph and post that runnable to be executed by the UI thread. Here is an example:
public class LoopExampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
thread_updater1s.start();
thread_updater2.start();
}// end of onCreate
Thread thread_updater1s = new Thread() {
#Override
public void run() {
try {
while (true) {
final Object data = getDataFromServer1();
handler.post(new Runnable() {
#Override
public void run() {
updateGraph1(data);
}
);
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread_updater2 = new Thread() {
#Override
public void run() {
try {
while (true) {
final Object data = getDataFromServer2();
handler.post(new Runnable() {
#Override
public void run() {
updateGraph2(data);
}
);
sleep(60000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Obviously, change that final Object data by the appropriate class that represents your data downloaded.
handler.post pushes the runnable onto the main (UI) thread's message queue for execution on the main thread.
So what you're doing is every sleep interval, you're sending a message to the main thread to run the function. Clearly, the main thread can't run 2 things at once, so that's why one runnable is delayed for the next one.
You probably want to do the work of the runnable in the separate threads - why did you start using a handler? What happens if you call do_1s_updates and do_large_updates directly instead of through the handler & runnable?
Note : I know there are many questions related to this, but still I am not convince, so asking.
I am getting cant create handler inside thread that has not called looper.prepare when I try to show the dialog.
Here is my code...
//this method is called from a different method based on some condition which is inturn called on click a button
private void download() {
thread = new Thread() {
public void run() {
/**** Downloads each tour's Tour.plist file ****/
try {
// do many heavy operations here, like download,
//calling web webvice and starting another activity
This comes at the end
Intent toAudio = new Intent(TourDescription.this,Audio.class);
startActivity(toAudio);
} catch (Exception e) {
}
}
};
thread.start();
}
Now before this actity gets called I am trying to show a dialog. I am trying to place that just before calling Intent.
Can any body please tell me how to do this, as I am not understanding how to solve this
you cannot show a dialog from a child thread.
A dialog can only be showed from within the UI thread/main Thread.
try this from inside the child thread
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO show dialog....
}
});