Android AsyncTask API przestaje działać w Androidzie 11. Jakie są alternatywy?

21

EDYCJA : To pytanie nie jest duplikatem jak

  1. Zaledwie kilka dni temu dokonano zatwierdzenia AOSP dotyczącego wycofania.
  2. Drugie pytanie dotyczy używania AsyncTaskLoader zamiast AsyncTask.


Google wycofuje interfejs API Android AsyncTask w systemie Android 11 i sugeruje jego użycie java.util.concurrent. możesz sprawdzić zatwierdzenie tutaj

 *
 * @deprecated Use the standard <code>java.util.concurrent</code> or
 *   <a href="https://developer.android.com/topic/libraries/architecture/coroutines">
 *   Kotlin concurrency utilities</a> instead.
 */
@Deprecated
public abstract class AsyncTask<Params, Progress, Result> {

Jeśli utrzymujesz starszą bazę kodu z asynchronicznymi zadaniami w Androidzie, prawdopodobnie będziesz musiał ją zmienić w przyszłości. Moje pytanie brzmi: jaka powinna być właściwa zamiana fragmentu kodu pokazanego poniżej przy użyciu java.util.concurrent. Jest to statyczna wewnętrzna klasa działania. Szukam czegoś, co będzie działaćminSdkVersion 16

private static class LongRunningTask extends AsyncTask<String, Void, MyPojo> {
        private static final String TAG = MyActivity.LongRunningTask.class.getSimpleName();
        private WeakReference<MyActivity> activityReference;

        LongRunningTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected MyPojo doInBackground(String... params) {
            // Some long running task

        }

        @Override
        protected void onPostExecute(MyPojo data) {

            MyActivity activity = activityReference.get();
            activity.progressBar.setVisibility(View.GONE);
            populateData(activity, data) ;
        }     


    }
Zeeshan
źródło
5
„Przestarzałe” oznacza, że ​​Google zaleca przejście do czegoś innego. Nie oznacza to, że klasa zostanie wkrótce usunięta. W szczególności AsyncTasknie można go usunąć bez naruszenia kompatybilności wstecznej.
CommonsWare
2
@ Style-7 nie jest.
EpicPandaForce,
Nie ma czegoś takiego jak „statyczna klasa wewnętrzna”. Masz na myśli statyczną klasę zagnieżdżoną.
beroal

Odpowiedzi:

22
private WeakReference<MyActivity> activityReference;

Dobra przeszkoda, że ​​jest przestarzała, ponieważ WeakReference<Context>zawsze był to hack, a nie właściwe rozwiązanie .

Teraz ludzie będą mieli możliwość oczyszczenia swojego kodu.


AsyncTask<String, Void, MyPojo> 

Oparty na tym kodzie, Progresstak naprawdę nie jest potrzebny, i jest Stringwejście + MyPojowyjście.

Jest to w rzeczywistości dość łatwe do osiągnięcia bez użycia AsyncTask.

public class TaskRunner {
    private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
    private final Handler handler = new Handler(Looper.getMainLooper());

    public interface Callback<R> {
        void onComplete(R result);
    }

    public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
        executor.execute(() -> {
            final R result = callable.call();
            handler.post(() -> {
                callback.onComplete(result);
            });
        });
    }
}

Jak przekazać ciąg? Tak jak:

class LongRunningTask implements Callable<MyPojo> {
    private final String input;

    public LongRunningTask(String input) {
        this.input = input;
    }

    @Override
    public MyPojo call() {
        // Some long running task
        return myPojo;
    }
}

I

// in ViewModel
taskRunner.executeAsync(new LongRunningTask(input), (data) -> {
    // MyActivity activity = activityReference.get();
    // activity.progressBar.setVisibility(View.GONE);
    // populateData(activity, data) ;

    loadingLiveData.setValue(false);
    dataLiveData.setValue(data);
});

// in Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main_activity);

    viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    viewModel.loadingLiveData.observe(this, (loading) -> {
        if(loading) {
            progressBar.setVisibility(View.VISIBLE);
        } else {
            progressBar.setVisibility(View.GONE);
        }
    });

    viewModel.dataLiveData.observe(this, (data) -> {
        populateData(data);
    }); 
}

W tym przykładzie użyto puli jednowątkowej, która jest dobra dla zapisów DB (lub serializowanych żądań sieciowych), ale jeśli chcesz czegoś do odczytu DB lub wielu żądań, możesz rozważyć następującą konfigurację modułu wykonującego:

private static final Executor THREAD_POOL_EXECUTOR =
        new ThreadPoolExecutor(5, 128, 1,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
EpicPandaForce
źródło
Pojawia się błąd executor.post. Nie można rozwiązać metody
Kris B
@KrisB najwyraźniej nazywa się execute()zamiastpost()
EpicPandaForce
Czy wiesz, czy możesz w to przejść Context? Wiem, że przejście Contextdo AsyncTaskjednego z nich było jednym z problemów.
Kris B
Nadal musisz uruchomić to w ViewModel, zachowanym fragmencie, singletonie lub czymkolwiek innym, jak zwykle w przypadku asynchronizacji w Androidzie.
EpicPandaForce
1
newSingleThreadExecutorjest lepszy dla zapisów, ale zdecydowanie powinieneś użyć THREAD_POOL_EXECUTORna końcu postu do odczytu bazy danych.
EpicPandaForce
3

Google zaleca korzystanie z platformy Java Concurrency lub Kotlin Coroutines. ale Rxjava ma znacznie większą elastyczność i funkcje niż współbieżność Java, dzięki czemu zyskała sporą popularność.

Wahab Khan Jadon
źródło