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.
Related
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);
This post says (in reference to the Android Doc) that any method on a view has to be called from the UI thread. However, I have not ran into any problem yet, though I set the OnClickListeners of Buttons in a non-UI-thread. Is this a situation of "You realy should not do this, even though you can." or is there a subset of methods that can actually be called from non-UI-threads?
If the latter is true, which operations are part of the subset?
EDIT
Example code:
Thread setUpActivity = new Thread(new Runnable() {
#Override
public void run() {
while (serviceConnection.getAppController() == null){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
btAddTag.setOnClickListener(onAddTag);
btGo.setOnClickListener(onGo);
runOnUiThread(new Runnable() {
#Override
public void run() {
setUpSpinner();
}
});
}
});
setUpActivity.start();
A quick answer would be:
You shouldn't do write operations on UI components from outside of the UI thread. This is because the UI components are not thread safe. And even if you might get away with a minor change on a device or emulator, you might get in trouble on other devices or in different situations.
Write operations would be:
set text, sizes, colors, etc.
I guess setting just a click listener won't get you into problems if you are not doing UI updates in the callback method(onClick..). But as a good practice I would advice not to do that(set the click listener on a non UI thread).
You can set listeners on non UI thread. Even if you really do not want it on non UI thread but on UI thread try using post method on view which will call on UI thread.
Usage :
view.post(new Runnable() {
public void run() {
// your action here on UI thread.
}
});
I'm calling methode doSomething() continuously with a thread.sleep(100). This happens in on the UIThread, so the UIthread gets unresponsive. What is the correct way in Android to run the method doSomething() or the entire someobject in a seperate thread?
public void loop(){
while(true){
someObject.doSomething();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Right now i'm using
new Thread(new Runnable() {
#Override
public void run() {
someObject.doSomething();
}
}).start();
This obviously creates a different thread for each iteration. I don't think this is the correct way. What is the correct way in Android?
new Thread(new Runnable() {
#Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
someObject.doSomething();
}
}
}).start();
Since run never returns, the thread will never end and will loop forever. It will call doSomething roughly every 100 ms (as close as sleep will get, which isn't exact).
You can make your own Thread class, with Looper and Handler, posting your doSomething every 100ms:
public class MyThread extends Thread{
private Handler myHandler;
#Override
public void run(){
Looper.prepare();
myHandler = new Handler();
myHandler.post(doSomethingRunnable);
}
Runnable doSomethingRunnable = new Runnnable{
doSomething(); //or myHandler.postDelayed() first for greater accuracy, but only if doSomething doesnt take too long
myHandler.postDelayed(doSomethingRunnable, 100);
};
doSomething(){
thisStuff(thatStuff());
}
}
You can use AsyncTask. doInBackground() is called on a background thread and will not block UI. This is the preferred way for doing stuff on background threads.
For a long running task. You can use an IntentService and put your background code in onHandleIntent
You can use a basic thread but it may be hard to manage. You can read more about threads in android here
I must use Thread in an Android project. Sometimes, it works corectly, however sometimes does not; it does not start (does not call SendToServer() method)or it starts but return to another function suddenly (return updated; line)before the thread does not finish.
Note: affected value is bigger than 0, it gives condition and it goes to if statement.
Here is the my code sample;
public static Boolean MyUpdateFunction(MyObject myobject){
Boolean updated=false;
//Code for updating local database
int affected= SqliteDb.update(....);
if(affected>0)
{
//Send updated data to server
//For this I must use Thread(I can't use AsyncThread)
updated=true;
SendToServer();
}
return updated;
}
public static void SendToServer()
{
try{
;
Thread th=new Thread(new Runnable() {
public void run() {
try {
//Create data and send it to server
//.......
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
th.start();
th.join();
}
catch(SQLException e)
{
Toast.makeText(myContext,"ERROR: "+e.getMessage(), Toast.LENGTH_LONG).show();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Other people are correct in that an AsyncTask is the way forward, but the direct problem due to what you're experiencing is this (and as such, I would recommend reading up on how Threading works):
When you start the thread, it begins a new process. The UI thread (which is generally where the majority of your code is) continues. So your code will fire the thread with SendToServer(), and then by definition will immediately return updated, as the UI thread immediately goes to the next line.
What you need is a callback from your Thread, which is handled in the onPostExecute() method of an AsyncTask. There's a good tutorial on how to use them and what they do here
Edit:
I've just seen from a comment above that you can't use Asynctasks, fair enough, but you still need a callback/event fired from your Thread to return any results
Instead of using threads and your variables (updated and affected), you can use AsyncTasks: see: http://developer.android.com/reference/android/os/AsyncTask.html
With AsyncTask, you have some methods which are doing exactly what you want:
onPreExecute
doInBackground
onPostExecute
So, what you can do is to check your condition in onPreExecute, then do your SendToServer in the doInBackground and onPostExecute do what you need.
I was trying to run a small worker thread in the middle of an activity which would get some stuff from a web service and fill a container when it returned.
new Thread() {
#Override
public void run() {
Log.w("thread", "can read from thread");
try {
MyActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.w("internal thread", "running");
}
});
}
catch (Exception e) {
// etc
}
}
}.start();
The "internal thread: running" would never print in the log, and when I ripped out the contents and just pasted it into the main body of the method, it worked. My conclusion is that the doubly nested thread never ran. Why would that happen? Am I misusing runOnUiThread?
Thanks!
Why would that happen? Am I misusing runOnUiThread?
Yeas ! runOnUiThread is other thread than Main thread. i suggest to have a look at Android Handler.