Sobes.tech
Junior

Jak wykonać kod w równoległym (nie głównym) wątku?

sobes.tech AI

Odpowiedź od AI

W celu równoległego wykonywania kodu na Androidzie używa się różnych mechanizmów, które pozwalają nie blokować głównego (UI) wątku i zachować responsywność aplikacji.

Wbudowane środki Android:

  1. Thread: Podstawowa klasa do tworzenia nowego wątku.

    new Thread(new Runnable() {
        @Override
        public void run() {
            // Kod do wykonania w równoległym wątku
        }
    }).start();
    

    Wada: Ręczne zarządzanie wątkami może prowadzić do problemów z cyklem życia i wyciekami pamięci.

  2. AsyncTask (Przestarzałe): Ułatwiało pracę z równoległymi wątkami i interakcję z wątkiem UI.

    private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            // Kod do wykonania w tle
            return null;
        }
    
        @Override
        protected void onPostExecute(Void result) {
            // Praca z wątkiem UI po doInBackground
        }
    }
    // Użycie: new MyAsyncTask().execute();
    

    Wady: Przestarzałe, ma ograniczenia i trudności w zarządzaniu cyklem życia.

  3. Handler i Looper: Pozwalają tworzyć wątki z pętlami obsługi wiadomości.

    HandlerThread handlerThread = new HandlerThread("MyWorkerThread");
    handlerThread.start();
    Handler handler = new Handler(handlerThread.getLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            // Kod do wykonania w wątku z Looperem
        }
    });
    

    Zaleta: Przydatne do powtarzających się zadań lub obsługi zdarzeń asynchronicznych w osobnym wątku.

  4. ExecutorService / ThreadPoolExecutor: Zapewniają pule wątków, co jest bardziej efektywne niż tworzenie nowego Thread dla każdego zadania.

    ExecutorService executor = Executors.newSingleThreadExecutor(); // lub newCachedThreadPool(), newFixedThreadPool(N)
    executor.execute(new Runnable() {
        @Override
        public void run() {
            // Kod do wykonania w wątku z puli
        }
    });
    // Nie zapomnij wywołać executor.shutdown() po zakończeniu
    

    Zalety: Zarządzają tworzeniem i ponownym używaniem wątków, zmniejszając koszty.

Nowoczesne podejścia:

  1. Kotlin Coroutines: Lekkie wątki, które oferują prostszy i czytelniejszy sposób programowania asynchronicznego.

    import kotlinx.coroutines.*
    
    // Uruchomienie korutyny w puli wątków (podobnie do wątku tła)
    GlobalScope.launch(Dispatchers.Default) {
        // Kod do wykonania w tle
    }
    
    // Uruchomienie korutyny dla operacji IO
    GlobalScope.launch(Dispatchers.IO) {
        // Kod do operacji IO (sieć, pliki itp.)
    }
    
    // Przełączenie na wątek UI do aktualizacji interfejsu
    GlobalScope.launch(Dispatchers.Default) {
        // Obliczenia w tle
        withContext(Dispatchers.Main) {
            // Aktualizacja UI
        }
    }
    

    Zalety: Lepsza czytelność kodu, bardziej efektywne wykorzystanie zasobów, prostota zarządzania anulowaniem zadań, ustrukturyzowana konkurencja.

  2. RxJava / RxKotlin: Programowanie reaktywne oparte na obserwowalnych strumieniach danych.

    import io.reactivex.rxjava3.core.Observable;
    import io.reactivex.rxjava3.schedulers.Schedulers;
    
    Observable.just("Jakiś wynik")
        .subscribeOn(Schedulers.io()) // Wykonaj w wątku IO
        .observeOn(AndroidSchedulers.mainThread()) // Obserwuj w wątku UI
        .subscribe(/* Obsługa wyniku */);
    

    Zalety: Potężne narzędzie do obsługi zdarzeń asynchronicznych i transformacji danych, ale wymaga wyższej krzywej uczenia.

Biblioteka WorkManager:

  1. WorkManager: Zalecany komponent Architecture Components do gwarantowanego wykonywania zadań w tle, nawet jeśli aplikacja jest zamknięta lub urządzenie zostanie zrestartowane.
    import androidx.work.*
    import java.util.concurrent.TimeUnit
    
    class MyWorker(appContext: Context, workerParams: WorkerParameters): Worker(appContext, workerParams) {
        override fun doWork(): Result {
            // Kod do wykonania zadania w tle
            return Result.success() // lub Result.failure(), Result.retry()
        }
    }
    
    // Tworzenie żądania wykonania zadania
    val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
        .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()) // Opcjonalne ograniczenia
        .build()
    
    // Umieszczenie zadania w kolejce
    WorkManager.getInstance(context).enqueue(workRequest)
    
    Zalety: Gwarantowane wykonanie, obsługa ograniczeń (typ sieci, bateria), zarządzanie anulowaniem, opóźnione wykonanie i powtórzenia.

Wybór konkretnej metody wykonywania kodu w wątku równoległym zależy od typu zadania, jego cyklu życia i wymagań dotyczących niezawodności. Dla większości nowoczesnych aplikacji Android zaleca się korzystanie z Kotlin Coroutines lub WorkManager.