Android AsyncTask API 在 Android 11 中弃用。有哪些替代方案?

Zee*_*han 120 java android kotlin kotlin-coroutines

Google 正在弃用 Android 11 中的 Android AsyncTask API,并建议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> {
Run Code Online (Sandbox Code Playgroud)

如果您在 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) ;
        }     


    }
Run Code Online (Sandbox Code Playgroud)

Epi*_*rce 87

private WeakReference<MyActivity> activityReference;
Run Code Online (Sandbox Code Playgroud)

很好,它已弃用,因为WeakReference<Context>总是一个 hack,而不是一个适当的解决方案

现在人们将有机会清理他们的代码。


AsyncTask<String, Void, MyPojo> 
Run Code Online (Sandbox Code Playgroud)

基于这段代码,Progress其实是不需要的,有String输入+MyPojo输出。

这实际上很容易在不使用 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);
            });
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

如何传入字符串?像这样:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

// 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);
    }); 
}
Run Code Online (Sandbox Code Playgroud)

这个例子使用了一个单线程池,它有利于数据库写入(或序列化网络请求),但如果你想要一些用于数据库读取或多个请求的东西,你可以考虑以下 Executor 配置:

private static final Executor THREAD_POOL_EXECUTOR =
        new ThreadPoolExecutor(5, 128, 1,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
Run Code Online (Sandbox Code Playgroud)

  • 在 TaskRunner 中,我收到编译器错误“在`callable.call()`处出现未处理的异常 java.lang.Exception` ...处理此问题的最佳方法是什么? (2认同)
  • 我真的很欣赏这个例子。谢谢!我最终几乎完全按原样使用它。我使用了一个“static”执行器,就像您在代码示例最后所示的那样,但仍然使用了“Executors.newSingleThreadExecutor()”。 (2认同)
  • 如果您确实需要取消,那么您必须使用 executor.submit 并取消 future,而不是 executor.execute。https://www.baeldung.com/java-future (2认同)

Kas*_*han 52

您可以直接使用java.util.concurrent包中的Executors 。

我还搜索了它,我在这个Android Async API is Deprecated帖子中找到了一个解决方案。

不幸的是,这篇文章使用的是 Kotlin,但经过一些努力,我已将其转换为 Java。所以这是解决方案。

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());

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

            //Background work here

            handler.post(new Runnable() {
                @Override
                public void run() {
                    //UI Thread work here
                }
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

很简单吧?如果您在项目中使用 Java8,则可以稍微简化一下。

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());

    executor.execute(() -> {
        //Background work here
        handler.post(() -> {
            //UI Thread work here
        });
    });
Run Code Online (Sandbox Code Playgroud)

尽管如此,它仍然无法打败 kotlin 代码的简洁性,但比以前的 java 版本要好。

希望这会帮助你。谢谢你

  • 实际上你可以再往下走一步: Executors.newSingleThreadExecutor().execute(() -&gt; dao.insert(data)); (7认同)
  • 我不明白这比正确使用 AsyncTask 有什么优势。您的方法和 AsyncTask 都使用 Java 并发包中的类似组件,并且上面显示的代码没有做任何事情来防止在 Activity 或 Fragment 上下文中使用 AsyncTask 时与不当使用相关的“内存泄漏”。 (4认同)
  • 这次真是万分感谢!它在调用服务器 API 时对我帮助很大。但是如果我想显示一些更新进度条怎么办?我可以把 onProgressUpdate 部分放在哪里? (2认同)
  • d= (◕‿↼) 简短而干净的答案,其中“干净”意味着没有“不要使用 `WeakReference&lt;Context&gt;` 而是使用 `LiveData` 的废话。 (2认同)

may*_*513 31

最简单的替代方法之一是使用 Thread

new Thread(new Runnable() {
    @Override
    public void run() {
        // do your stuff
        runOnUiThread(new Runnable() {
            public void run() {
                // do onPostExecute stuff
            }
        });
    }
}).start();
Run Code Online (Sandbox Code Playgroud)

如果您的项目支持 JAVA 8,则可以使用 lambda

new Thread(() -> {
        // do background stuff here
        runOnUiThread(()->{
            // OnPostExecute stuff here
        });
    }).start();
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案有几个缺点。首先,线程保留对活动的引用,这可能会泄漏上下文并使应用程序崩溃。其次,我们不能从片段中使用它。第三,我们无法更新后台任务的进度,第四,无法取消线程。最后,它在应用程序中创建了许多样板代码。 (10认同)
  • @SonTruong 我对这些缺点有一些真正的问题。1:如果没有明确传递,线程如何/为什么保留活动的引用?我理解 `runOnUiThread`,但是对于短任务,这应该不是问题,不是吗?3:后台任务的进度不能简单地通过`runOnUiThread`中的相应调用来处理,就像`publishProgress`/`onProgressionUpdate`一样?4:继续查看“AsyncTask”和“FutureTask”代码,它所做的就是使用“Thread.interrupt”功能来创建“cancel”功能。难道不能用这种方法做同样的事情吗? (3认同)
  • 当后台调用时如何显示百分比? (2认同)

Ser*_*gey 20

根据Android 文档 AsyncTask已在API 级别 30 中弃用,建议改用标准 java.util.concurrent 或Kotlin 并发实用程序

使用后者可以非常简单地实现:

  1. 在 上创建通用扩展函数CoroutineScope

     fun <R> CoroutineScope.executeAsyncTask(
             onPreExecute: () -> Unit,
             doInBackground: () -> R,
             onPostExecute: (R) -> Unit
     ) = launch {
         onPreExecute() // runs in Main Thread
         val result = withContext(Dispatchers.IO) { 
             doInBackground() // runs in background thread without blocking the Main Thread
         }
         onPostExecute(result) // runs in Main Thread
     } 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将该函数与 any 一起使用CoroutineScope

    • ViewModel

      class MyViewModel : ViewModel() {
      
          fun someFun() {
              viewModelScope.executeAsyncTask(onPreExecute = {
                  // ... runs in Main Thread
              }, doInBackground = {
                  // ... runs in Worker(Background) Thread
                  "Result" // send data to "onPostExecute"
              }, onPostExecute = {
                  // runs in Main Thread
                  // ... here "it" is the data returned from "doInBackground"
              })
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)
    • ActivityFragment

      lifecycleScope.executeAsyncTask(onPreExecute = {
          // ... runs in Main Thread
      }, doInBackground = {
          // ... runs in Worker(Background) Thread
          "Result" // send data to "onPostExecute"
      }, onPostExecute = {
          // runs in Main Thread
          // ... here "it" is the data returned from "doInBackground"
      })
      
      Run Code Online (Sandbox Code Playgroud)

    要在应用程序的build.gradle文件的依赖项中使用viewModelScopelifecycleScope添加下一行:

    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
    
    Run Code Online (Sandbox Code Playgroud)

    在撰写本文时 final LIFECYCLE_VERSION = "2.3.0-alpha05"

更新

我们也可以使用onProgressUpdate函数实现进度更新:

fun <P, R> CoroutineScope.executeAsyncTask(
        onPreExecute: () -> Unit,
        doInBackground: suspend (suspend (P) -> Unit) -> R,
        onPostExecute: (R) -> Unit,
        onProgressUpdate: (P) -> Unit
) = launch {
    onPreExecute()

    val result = withContext(Dispatchers.IO) {
        doInBackground {
            withContext(Dispatchers.Main) { onProgressUpdate(it) }
        }
    }
    onPostExecute(result)
}
Run Code Online (Sandbox Code Playgroud)

使用 any CoroutineScope(见上面的实现)我们可以调用它:

someScope.executeAsyncTask(
    onPreExecute = {
        // ... runs in Main Thread
    }, doInBackground = { publishProgress: suspend (progress: Int) -> Unit ->
        
        // ... runs in Background Thread

        // simulate progress update
        publishProgress(50) // call `publishProgress` to update progress, `onProgressUpdate` will be called
        delay(1000)
        publishProgress(100)

        
        "Result" // send data to "onPostExecute"
    }, onPostExecute = {
        // runs in Main Thread
        // ... here "it" is a data returned from "doInBackground"
    }, onProgressUpdate = {
        // runs in Main Thread
        // ... here "it" contains progress
    }
)
Run Code Online (Sandbox Code Playgroud)

  • java中有这个解决方案吗? (2认同)

mil*_*dia 10

使用此类在后台线程中执行后台任务此类适用于包括 Android 11 在内的所有 android API 版本,此代码与使用doInBackgroundonPostExecute方法的AsyncTask相同

public abstract class BackgroundTask {

    private Activity activity;
    public BackgroundTask(Activity activity) {
        this.activity = activity;
    }

    private void startBackground() {
        new Thread(new Runnable() {
            public void run() {

                doInBackground();
                activity.runOnUiThread(new Runnable() {
                    public void run() {

                        onPostExecute();
                    }
                });
            }
        }).start();
    }
    public void execute(){
        startBackground();
    }

    public abstract void doInBackground();
    public abstract void onPostExecute();

}
Run Code Online (Sandbox Code Playgroud)

复制上面的类后,您可以使用它:

new BackgroundTask(MainActivity.this) {
        @Override
        public void doInBackground() {

            //put you background code
            //same like doingBackground
            //Background Thread
        }

        @Override
        public void onPostExecute() {

            //hear is result part same
            //same like post execute
            //UI Thread(update your UI widget)
        }
    }.execute();
Run Code Online (Sandbox Code Playgroud)


小智 6

在这里,我使用协程为 AsyncTask 创建了一个替代方案,它可以与 AsyncTask 一样使用,而无需更改项目中的大量代码库。

  1. 创建一个新的抽象类 AsyncTaskCoroutine,它接受输入参数和输出参数数据类型——当然这些参数是可选的 :)

     import kotlinx.coroutines.Dispatchers
     import kotlinx.coroutines.GlobalScope
     import kotlinx.coroutines.async
     import kotlinx.coroutines.launch
    
     abstract class AsyncTaskCoroutine<I, O> {
         var result: O? = null
         //private var result: O
         open fun onPreExecute() {}
    
         open fun onPostExecute(result: O?) {}
         abstract fun doInBackground(vararg params: I): O
    
         fun <T> execute(vararg input: I) {
             GlobalScope.launch(Dispatchers.Main) {
                 onPreExecute()
                 callAsync(*input)
             }
         }
    
         private suspend fun callAsync(vararg input: I) {
             GlobalScope.async(Dispatchers.IO) {
                 result = doInBackground(*input)
             }.await()
             GlobalScope.launch(Dispatchers.Main) {
    
                 onPostExecute(result)
    
    
             }
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)

2 . 现在在 Activity 内部使用它与您的旧 AsycnTask 相同

 new AsyncTaskCoroutine() {
                @Override
                public Object doInBackground(Object[] params) {
                    return null;
                }
    
                @Override
                public void onPostExecute(@Nullable Object result) {
    
                }
    
                @Override
                public void onPreExecute() {
    
                }
            }.execute();
Run Code Online (Sandbox Code Playgroud)
  1. InCase 如果您需要发送传递参数

      new AsyncTaskCoroutine<Integer, Boolean>() {
    
         @Override
         public Boolean doInBackground(Integer... params) {
             return null;
         }
    
         @Override
         public void onPostExecute(@Nullable Boolean result) {
    
         }
    
         @Override
         public void onPreExecute() {
    
         }
     }.execute();
    
    Run Code Online (Sandbox Code Playgroud)

  • 请不要使用 kotlin,首先使用 Java,然后也许 Kotlin 作为正在使用它的人的替代品。谢谢 (5认同)
  • @Darksymphony我完全不同意你的观点,这个问题在使用Java方面已经很老了。如果您仍在 Android 上使用 Java,则需要重新考虑您的选择。他为 AsynTask 编写了一个非常好的替代方案。 (4认同)
  • 希望 Java 能够在未来 X 年内作为 Android 的基本语言继续存在。某个懒惰的人带来了 Kotlin,并用有趣的命令强迫 Android 开发人员使用它:)也许有一天我会重新考虑。但只要我们有选择,我就会选择 Java (4认同)
  • @Darksymphony Kotlin 是最优秀的语言,毫无疑问。你必须适应时代。 (2认同)

Ano*_*p M 5

Android在 Android 11 中弃用了AsyncTask API,以解决一些问题。

那么,现在怎么样了?

  • 线程
  • 执行者
  • RxJava
  • 可听期货
  • 协程

为什么是协程?

协程是 Kotlin 进行异步编程的方式。自 Kotlin 1.3 起,编译器支持稳定,并带有一个 kotlinx.coroutines库 -

  • 结构化并发
  • 非阻塞,顺序代码
  • 取消传播
  • 自然异常处理


Rus*_*hts 5

接受的答案很好。但是...我没有看到cancel()方法的实现

因此,我的实现可以取消正在运行的任务(模拟取消)如下。需要取消才能在任务中断的情况下不运行 postExecute() 方法。

public abstract class AsyncTaskExecutor<Params> {
    public static final String TAG = "AsyncTaskRunner";

    private static final Executor THREAD_POOL_EXECUTOR =
            new ThreadPoolExecutor(5, 128, 1,
                    TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private boolean mIsInterrupted = false;

    protected void onPreExecute(){}
    protected abstract Void doInBackground(Params... params);
    protected void onPostExecute(){}
    protected void onCancelled() {}

    @SafeVarargs
    public final void executeAsync(Params... params) {
        THREAD_POOL_EXECUTOR.execute(() -> {
            try {
                checkInterrupted();
                mHandler.post(this::onPreExecute);

                checkInterrupted();
                doInBackground(params);

                checkInterrupted();
                mHandler.post(this::onPostExecute);
            } catch (InterruptedException ex) {
                mHandler.post(this::onCancelled);
            } catch (Exception ex) {
                Log.e(TAG, "executeAsync: " + ex.getMessage() + "\n" + Debug.getStackTrace(ex));
            }
        });
    }

    private void checkInterrupted() throws InterruptedException {
        if (isInterrupted()){
            throw new InterruptedException();
        }
    }

    public void cancel(boolean mayInterruptIfRunning){
        setInterrupted(mayInterruptIfRunning);
    }

    public boolean isInterrupted() {
        return mIsInterrupted;
    }

    public void setInterrupted(boolean interrupted) {
        mIsInterrupted = interrupted;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此类的示例:

public class MySearchTask extends AsyncTaskExecutor<String> {

    public MySearchTask(){
    }

    @Override
    protected Void doInBackground(String... params) {
        // Your long running task
        return null;
    }

    @Override
    protected void onPostExecute() {
        // update UI on task completed
    }

    @Override
    protected void onCancelled() {
        // update UI on task cancelled
    }
}

MySearchTask searchTask = new MySearchTask();
searchTask.executeAsync("Test");
Run Code Online (Sandbox Code Playgroud)

  • 您对“mIsInterrupted”的使用不是线程安全的。要么它必须是原子/易失性的,要么使用它的方法必须是同步的。 (2认同)

Top*_*ter 5

AsyncTask类似乎不会很快被删除,但我们只是简单地取消了它的弃用,因为:

  • 我们不想添加大量抑制注释。
  • 替代解决方案有太多的样板,或者在大多数情况下,与AsyncTask.
  • 我们不想重新发明轮子。
  • 我们不想担心有一天它最终会被删除。
  • 重构需要太多时间。

例子

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

您可能已经知道,这没什么大不了的,而且基本上是众所周知的AndroidX图书馆一直在做的事情。

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