Android 11中已经弃用了AsyncTask API,有哪些替代方案?

231

谷歌正在逐步淘汰Android AsyncTask API,建议在Android 11中使用java.util.concurrent代替。您可以查看此提交

 *
 * @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> {
如果您正在维护一个在Android中具有异步任务的旧代码库,那么未来可能需要更改它。 我的问题是:使用java.util.concurrent,以下代码片段的适当替换应该是什么? 它是Activity的静态内部类。 我正在寻找适用于minSdkVersion 16的解决方案。

如果您正在维护一个在Android中具有异步任务的旧代码库,那么未来可能需要更改它。 我的问题是:使用java.util.concurrent,以下代码片段的适当替换应该是什么? 它是Activity的静态内部类。 我正在寻找适用于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) ;
        }     


    }

45
“被弃用”意味着Google建议您转移到其他东西。这并不意味着该类很快就会被删除。特别地,AsyncTask不能被删除,否则会破坏向后兼容性。 - CommonsWare
11
@Style-7 不是。 - EpicPandaForce
28
这太糟糕了。建议使用来自官方Android文档的“AsyncTask”。我之前是后端开发人员,已经熟悉了executorService。基于这个建议,我将所有后台任务迁移到了使用“AsyncTask”。现在他们告诉我们不要再使用它了? - Kimi Chiu
1
@CommonsWare 从Android 11开始,Google开始删除已弃用的方法。 - Duna
3
@Addy:我在这里评论的具体问题是AsyncTask删除,这样做会破坏许多现有的应用程序。程序员应该学习除了AsyncTask之外的其他技术(如RxJava、Kotlin协程等),因为它们更好,并且在专业环境中使用得更广泛。 - CommonsWare
显示剩余8条评论
20个回答

4

1
这看起来不错。但缺少一件事。可以取消吗? - chitgoks
1
@Idan Damri,你的解释太棒了。在Kotlin中帮助我很多,让我用更少的代码实现了异步任务。 - Smack Alpha
@chitgoks 有人在评论中问了,我已经回答了。 看一下 =] - Idan Damri
@SmackAlpha 这是无意义的内容。 - Idan Damri

4

我自己定制的替代方案:https://github.com/JohnyDaDeveloper/AndroidAsync

它仅在应用程序运行时(更具体地说,是安排任务的活动)才起作用,但能够在后台任务完成后更新UI。

编辑:我的AsyncTask不再需要Activity来运行。


哇,不错啊,但这不是 Google 在他们的 AsyncTask 中使用的实现方式吗?抱歉,我不知道他们是如何实现的。 - Acuna

4

AsyncTask 类似乎不会很快被删除,但我们仍然不得不取消弃用,因为:

  • 我们不想添加太多的压制注释。
  • 替代方案有太多样板代码,或在大多数情况下与 AsyncTask 相比没有任何真正的优势。
  • 我们不想重新发明轮子。
  • 我们不想害怕它最终会被删除的那一天。
  • 重构需要太多时间。

例子

只需将以下文件添加到您的项目中,然后搜索 "android.os.AsyncTask" 导入,并将所有内容替换为您为该文件选择的包。

正如您可能已经知道的那样,这并不是什么大不了的事,基本上也是众所周知的 AndroidX 库一直在做的事情。

获取AsyncTask.java文件: https://gist.github.com/top-master/0efddec3e2c35d77e30331e8c3bc725c


3

只需将整个类替换为此线程,并将其放入方法中以传递变量。

new Thread(() -> {
            // do background stuff here
            runOnUiThread(()->{
                // OnPostExecute stuff here
              
            });
        }).start();

在Fragment中,将Context添加到runOnUiThread()方法中:

 new Thread(() -> {
            // do background stuff here
            context.runOnUiThread(()->{
                // OnPostExecute stuff here
            });
        }).start();

线程的参数能否在不创建新类的情况下添加? - dcarl661

3
您可以使用这个自定义类作为AsyncTask<>的替代,它与AsyncTask相同,因此您不需要为相同的任务额外付出努力。
import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TaskRunner {

    private static final int CORE_THREADS = 3;
    private static final long KEEP_ALIVE_SECONDS = 60L;
    private static TaskRunner taskRunner = null;
    private Handler handler = new Handler(Looper.getMainLooper());
    private ThreadPoolExecutor executor;

    private TaskRunner() {
        executor = newThreadPoolExecutor();
    }

    public static TaskRunner getInstance() {
        if (taskRunner == null) {
            taskRunner = new TaskRunner();
        }
        return taskRunner;
    }

    public void shutdownService() {
        if (executor != null) {
            executor.shutdown();
        }
    }

    public void execute(Runnable command) {
        executor.execute(command);
    }

    public ExecutorService getExecutor() {
        return executor;
    }

    public <R> void executeCallable(@NonNull Callable<R> callable, @NonNull OnCompletedCallback<R> callback) {
        executor.execute(() -> {
            R result = null;
            try {
                result = callable.call();
            } catch (Exception e) {
                e.printStackTrace(); // log this exception
            } finally {
                final R finalResult = result;
                handler.post(() -> callback.onComplete(finalResult));
            }
        });
    }

    private ThreadPoolExecutor newThreadPoolExecutor() {
        return new ThreadPoolExecutor(
                CORE_THREADS,
                Integer.MAX_VALUE,
                KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                new SynchronousQueue<>()
        );
    }

    public interface OnCompletedCallback<R> {
        void onComplete(@Nullable R result);
    }
}

如何使用它?请按照以下示例进行操作。
使用lambda表达式:
TaskRunner.getInstance().executeCallable(() -> 1, result -> {
});


TaskRunner.getInstance().execute(() -> {
});

没有lambda表达式

TaskRunner.getInstance().executeCallable(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 1;
    }
}, new TaskRunner.OnCompletedCallback<Integer>() {
    @Override
    public void onComplete(@Nullable Integer result) {

    }
});

TaskRunner.getInstance().execute(new Runnable() {
    @Override
    public void run() {

    }
});

注意:不要忘记关闭执行器服务。
TaskRunner.getInstance().shutdownService();

我改变了主意,这绝对是我找到的最好、最简单的实现方式。不过,我有几个问题:在线程仍然活动的情况下调用shutdown是否安全(类似于取消操作)?如果我忘记关闭它,它会在60秒(您的默认设置)的不活动时间后自动关闭吗?我也会研究代码并尽力自己回答这些问题,但多线程对我来说有点难以理解(希望在研究代码后能理解)。谢谢。 - Trasd
找到我需要的了。再次感谢。 - Trasd

2

你可以使用ExecutorsCompletableFutures

这是辅助类:

public class Async {

    private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

    private static final HandlerExecutor THREAD_MAIN = new HandlerExecutor(Looper.getMainLooper());

    public static <T> void execute(Supplier<T> async, Consumer<T> sync) {
        CompletableFuture.supplyAsync(async, THREAD_POOL).thenAcceptAsync(sync, THREAD_MAIN);
    }

    public static void execute(Runnable async) {
        CompletableFuture.runAsync(async);
    }

}

这里是一个关于如何使用它的示例:

public static void main(String[] args) {
    String url = "https://example.com/";

    Async.execute(() -> {
        //do something async without result
    });

    Async.execute(
            () -> {
                //do something async and return result
                try {
                    HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                    return connection.getResponseCode();
                } catch (Exception e) {
                    return null;
                }
            },
            (responseCode) -> {
                //do something with result on main thread
                Log.e("Async", "responseCode=" + responseCode);
            }
    );
}

1
在我看来,除非你展示了如何传递字符串url,否则这段代码是不完整的。 - dcarl661

2

根据你的需求,你可以迁移到以下几种方法:

  • 线程 + 处理程序(Thread + Handler)
  • 执行器(Executor)
  • 未来(Future)
  • IntentService
  • 作业调度器(JobScheduler)
  • RxJava
  • 协程(Kotlin)

[Android异步变体]


只需取消弃用AsyncTask - 因为如果它符合我们的需求,我们早就使用了上面提到的其中一个,如果我们真的不需要AsyncTasks的功能的话;-) - Top-Master

0

这是我的代码

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public abstract class AsyncTaskRunner<T> {

    private ExecutorService executorService = null;
    private Set<Callable<T>> tasks = new HashSet<>();

    public AsyncTaskRunner() {
        this.executorService = Executors.newSingleThreadExecutor();
    }
    
    public AsyncTaskRunner(int threadNum) {
        this.executorService = Executors.newFixedThreadPool(threadNum);
    }


    public void addTask(Callable<T> task) {
        tasks.add(task);
    }

    public void execute() {
        try {
            List<Future<T>> features = executorService.invokeAll(tasks);

            List<T> results = new ArrayList<>();
            for (Future<T> feature : features) {
                results.add(feature.get());
            }
            this.onPostExecute(results);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            this.onCancelled();
        } finally {
            executorService.shutdown();
        }

    }

    protected abstract void onPostExecute(List<T> results);

    protected void onCancelled() {
        // stub
    }

}

并提供使用示例。 扩展AsyncTaskRunner类,

class AsyncCalc extends AsyncTaskRunner<Integer> {

    public void addRequest(final Integer int1, final Integer int2) {
        this.addTask(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // Do something in background
                return int1 + int2;
            }
        });
    }

    @Override
    protected void onPostExecute(List<Integer> results) {
        for (Integer answer: results) {
            Log.d("AsyncCalc", answer.toString());
        }
    }
}

那就使用它吧!

AsyncCalc calc = new AsyncCalc();
calc.addRequest(1, 2);
calc.addRequest(2, 3);
calc.addRequest(3, 4);
calc.execute();

0
WorkManager是一个很好的选择。
OneTimeWorkRequest mywork = new OneTimeWorkRequest.Builder(FlipMediaPeriodicWorker.class)
                .setInitialDelay(activity.currentMedia.timing, TimeUnit.SECONDS)
                .build(); 
WorkManager.getInstance(activity).enqueue(mywork);

你可以将其递归,以便重复执行少于15分钟的任务。

-3

文档显示:

AsyncTask 这个类在API级别30中已被弃用。请改用标准的java.util.concurrent或Kotlin并发工具。

您需要使用Handler或协程代替AsyncTask

Java中使用Handler

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        // Your Code
    }
}, 3000);

使用 Handler 进行 Kotlin 编程

Handler(Looper.getMainLooper()).postDelayed({
    // Your Code
}, 3000)

这个答案在同一个主线程上执行代码,而不像AsyncTask那样在不同的线程上执行。 - gxcare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接