Sobes.tech
Junior

How to execute code in a parallel thread (not the main one)?

sobes.tech AI

Answer from AI

On Android, there are several ways to execute code in a parallel thread other than the main one:

  1. ExecutorService and Runnable / Callable: This is a flexible and powerful way to manage thread pools. It allows reusing threads and controlling their number.

    ExecutorService executor = Executors.newSingleThreadExecutor(); // Or newFixedThreadPool(int n)
    executor.execute(new Runnable() {
        @Override
        public void run() {
            // Code to execute in a parallel thread
        }
    });
    
    // To get results, use Callable and Future
    Future<String> future = executor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            // Code to execute in a parallel thread
            return "Result";
        }
    });
    // To get the result: String result = future.get(); // Blocking call
    
  2. AsyncTask (deprecated): Previously popular, but now deprecated class for executing background operations with progress and result publishing in the main thread.

    // Example usage (deprecated, not recommended for new projects)
    /*
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... voids) {
            // Code to run in background
            return null;
        }
    
        @Override
        protected void onPostExecute(Void aVoid) {
            // Code to run in main thread after doInBackground
        }
    }.execute();
     */
    
  3. Handlers (Handler) and Looper: Create a thread with its own Looper, allowing to send and process messages.

    HandlerThread handlerThread = new HandlerThread("MyWorkerThread");
    handlerThread.start();
    Handler handler = new Handler(handlerThread.getLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            // Code to execute in HandlerThread
        }
    });
    
  4. ExecutorService / ThreadPoolExecutor: Thread pools that are more efficient than creating a new Thread for each task.

    ExecutorService executor = Executors.newSingleThreadExecutor(); // Or newCachedThreadPool(), newFixedThreadPool(N)
    executor.execute(new Runnable() {
        @Override
        public void run() {
            // Code to execute in a thread from the pool
        }
    });
    // Remember to call executor.shutdown() when done
    

Modern approaches:

  1. Kotlin Coroutines: Lightweight threads providing a simpler and more readable way of asynchronous programming.

    import kotlinx.coroutines.*
    
    // Launch a coroutine in a thread pool (background thread)
    GlobalScope.launch(Dispatchers.Default) {
        // Code to run in background
    }
    
    // Launch a coroutine for IO operations
    GlobalScope.launch(Dispatchers.IO) {
        // Code for IO tasks (network, files, etc.)
    }
    
    // Switch to UI thread for UI updates
    GlobalScope.launch(Dispatchers.Default) {
        // Background computations
        withContext(Dispatchers.Main) {
            // UI update
        }
    }
    

Advantages: Improved code readability, efficient resource use, easy cancellation, structured concurrency.

  1. RxJava / RxKotlin: Reactive programming based on observable data streams.

    import io.reactivex.rxjava3.core.Observable;
    import io.reactivex.rxjava3.schedulers.Schedulers;
    
    Observable.just("Some result")
        .subscribeOn(Schedulers.io()) // Run in IO thread
        .observeOn(AndroidSchedulers.mainThread()) // Observe on UI thread
        .subscribe(/* handle result */);
    

For tasks that need guaranteed execution even if the app is closed or for synchronization, consider using WorkManager:

  1. WorkManager: Recommended component for guaranteed background task execution, even if the app is closed or device rebooted.

    import androidx.work.*
    import java.util.concurrent.TimeUnit
    
    class MyWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
        override fun doWork(): Result {
            // Code for background task
            return Result.success() // or Result.failure(), Result.retry()
        }
    }
    
    // Create a work request
    val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()) // Optional constraints
        .build()
    
    // Enqueue the task
    WorkManager.getInstance(context).enqueue(workRequest)
    

Choosing the specific method depends on the task type, lifecycle, and reliability requirements. For most modern Android apps, ExecutorService (Java) and Coroutines (Kotlin) are preferred.