Home > front end >  Android: How to show a spinner while performing a long-running operation?
Android: How to show a spinner while performing a long-running operation?

Time:02-08

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.

CodePudding user response:

For running long task operations, you should use Worker Thread. You must run your task in worker thread and then return task results to UI.

First method is using AsyncTask:

AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes. It also has inconsistent behavior on different versions of the platform, swallows exceptions from doInBackground, and does not provide much utility over using Executors directly.

Second one is using Pure Thread

All Android apps use a main thread to handle UI operations. Calling long-running operations from this main thread can lead to freezes and unresponsiveness. For example, if your app makes a network request from the main thread, your app's UI is frozen until it receives the network response. You can create additional background threads to handle long-running operations while the main thread continues to handle UI updates.

official document

and the last method is using Coroutine (just for kotlin)

if you are using kotlin i suggest this one otherwise use AsyncTask

Just try to run your tasks in other Thread and return results by using a callback like interface

CodePudding user response:

first of all I recommend that get familiar with threads in android :

https://developer.android.com/guide/components/processes-and-threads

then , there is some lib to manage threads

AsyncTask , Couroutin , Rxjava . . .

CodePudding user response:

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().

  •  Tags:  
  • Related