I have a ProgressBar in .xml that I want to show when a long-running operation. I use
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
to set its visibility in some onButtonClick method. If the above code is all that is in the method, it works just fine. The problem is when I have a method like this:
public void onButtonClick (android.view.View view){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
longRunningMethod(); // This method takes 5-10 seconds to run
progressSpinner.setVisibility(View.GONE);
}
The UI just locks up until longRunningMethod is done. That method works just fine, but the spinner never shows.
I tried running everything on a different thread with this:
public void onButtonClick (android.view.View view){
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(this::longRunningMethod);
}
And I added the spinner visibility changing stuff to longRunningMethod:
private void longRunningMethod(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
// Logic that takes 5-10 seconds to run.
progressSpinner.setVisibility(View.GONE);
}
When I do this, the UI doesn't lock up, but nothing in longRunningMethod works. The spinner won't show and the logic also doesn't seem to work, although this may just be a problem with that logic not playing nice on not-the-UI-thread. I am very confused that the spinner visibility won't update from here though.
Taking Sina's suggestion to use a Thread, the class now looks like:
public class MainActivity extends AppCompatActivity{
// Irrelevant code (fields, init, other button handlers, etc...)
private void onButtonClick(android.view.View view){
LongRunningThread longRunningThread = new LongRunningThread();
longRunningThread.start();
}
private class LongRunningThread extends Thread{
public void run(){
runOnUiThread(MainActivity.this::showSpinner);
// Logic that takes 5-10 seconds to run
runOnUiThread(MainActivity.this::hideSpinner);
}
}
private void showSpinner(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
}
private void hideSpinner(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.GONE);
}
}
Doing this shows the progress spinner while the long-running logic is running and then hides the progress spinner after the long-running logic has completed. The UI stays responsive throughout and doesn't lock up.
Compared to the original code, this uses Thread instead of ExecutorService. It also runs UI logic via AppCompatActivity.runOnUiThread().
It would seem the answer to doing any long-running tasks alongside UI updates, without locking up the UI, is to create a Thread and call Thread.start(). The Thread should contain the long-running logic as well as the UI logic. The UI logic within that Thread must be run using runOnUiThread().