【问题标题】:The AsyncTask API is deprecated in Android 11. What are the alternatives?Android AsyncTask API 在 Android 11 中弃用。有哪些替代方案?
【发布时间】:2020-03-05 03:44:06
【问题描述】:

Google 正在 Android 11 中弃用 Android AsyncTask API,并建议改用 java.util.concurrent。你可以查看提交here

 *
 * @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 正确替换下面显示的代码 sn-p。它是 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) ;
        }     


    }

【问题讨论】:

  • “已弃用”表示 Google 建议您改用其他方式。这并不意味着该类将很快被删除。特别是,AsyncTask 不能在不破坏向后兼容性的情况下被删除。
  • @Style-7 不是。
  • 这是一场灾难。推荐使用 Android 官方文档中的AsyncTask。我是一名后端开发人员,已经熟悉 executorService。对于这个建议,我迁移了所有后台任务以使用AsyncTask。现在他们告诉我们不要使用它?
  • @Duna:有什么例子吗?他们从库中删除不推荐使用的方法,因为开发人员控制他们使用的库的版本。但是,正如我所指出的,如果不破坏向后兼容性,就无法删除 AsyncTask
  • @Addy:我在这里评论的具体问题是AsyncTask删除,如果不破坏大量现有应用程序,这是不可能发生的。程序员应该学习 AsyncTask 以外的其他技术(RxJava、Kotlin 协程等),因为它们更好,并且在这一点上更广泛地用于专业环境。

标签: java android kotlin android-asynctask kotlin-coroutines


【解决方案1】:

在这里,我还使用抽象类创建了 AsyncTask 的替代方案,它可以直接复制为一个类。

/app/src/main/java/../AsyncTasks.java

public abstract class AsyncTasks {
    private final ExecutorService executors;

    public AsyncTasks() {
        this.executors = Executors.newSingleThreadExecutor();
    }

    private void startBackground() {
        onPreExecute();
        executors.execute(new Runnable() {
            @Override
            public void run() {
                doInBackground();
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        onPostExecute();
                    }
                });
            }
        });
    }

    public void execute() {
        startBackground();
    }

    public void shutdown() {
        executors.shutdown();
    }

    public boolean isShutdown() {
        return executors.isShutdown();
    }

    public abstract void onPreExecute();

    public abstract void doInBackground();

    public abstract void onPostExecute();
}

上述类的实现/使用

new AsyncTasks() {
     @Override
            public void onPreExecute() { 
             // before execution
      }

     @Override
            public void doInBackground() {
              // background task here 
            }

     @Override
            public void onPostExecute() { 
             // Ui task here
            }
     }.execute();

【讨论】:

  • 为什么你仍然用异步任务命名你的解决方案,即使你使用 ExecutorService。
  • 只是为了初学者的简单
  • 在用户 Attaullah 的上述代码中,是否可以为通过该类创建的每个后台任务分配特定名称?这样可以更轻松地在 Android Studio 的分析器中分析后台任务。
  • 是的,你可以很容易地给它起个名字。
  • 我添加了“Thread.currentThread().setName(threadName);”就在“public void run()”下面,我正在通过 Execute 方法传递 threadName。效果很好。
【解决方案2】:

接受的答案很好。但... 没看到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;
    }
}

使用该类的示例:

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");

【讨论】:

  • 您对mIsInterrupted 的使用不是线程安全的。要么它必须是原子的/易失的,要么使用它的方法必须是同步的。
【解决方案3】:

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

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();

}

复制上面的类之后,你就可以用这个了:

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();

【讨论】:

    【解决方案4】:

    这是我的代码

    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();
    

    【讨论】:

      【解决方案5】:

      我实际上写了两个关于它的 Medium 故事:

      第一个是 Java 和 Runnable 的解决方法,第二个是 Kotlin 和协程解决方案。 当然,两者都有代码示例。

      【讨论】:

      • 这看起来不错。不过缺一件事。可以取消吗?
      • @Idan Damri,你的解释太棒了。在 kotlin 中用更少的代码帮助我完成了类似的异步任务
      • @chitgoks 有人在 cmets 中提问,我已经回答了。看看=]
      【解决方案6】:

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

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

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

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

      【讨论】:

      • 后台调用时如何显示百分比?
      • 您需要使用 runOnUiThread 来更新进度条或任何其他用于更新/显示已完成任务百分比的机制。
      • 这个解决方案有几个缺点。首先,线程保持对活动的引用,这可能会泄漏上下文并使应用程序崩溃。其次,我们不能从片段中使用它。第三,我们无法更新后台任务的进度,第四,没有办法取消线程。最后,它会在应用程序中创建许多样板代码。
      • 您的代码甚至无法编译......我认为有一些拼写错误,例如当您说new Runnable({...}) 时,您的意思是new Runnable(){...}。因为第一个就像你调用一个构造函数并传递一个数组初始化器,触发编译器错误。第二种是创建匿名内部类的正确方法
      • @SonTruong 我对这些缺点有几个真正的问题。 1:如果没有明确传递,线程如何/为什么保留活动的引用?我了解runOnUiThread,但是对于短期任务,这应该不是问题,不是吗? 3:后台任务的进度不能像publishProgress/onProgressionUpdate那样简单的通过runOnUiThread内的调用来处理吗? 4:向下AsyncTaskFutureTask 代码,它所做的只是使用Thread.interrupt 功能来创建cancel 功能。这种方法不能做同样的事情吗?
      【解决方案7】:

      您可以迁移到下一个方法,具体取决于您的需求

      • 线程 + 处理程序
      • 执行者
      • 未来
      • 意图服务
      • 作业调度器
      • RxJava
      • 协程 (Kotlin)

      [Android async variants]

      【讨论】:

      • 从 AsyncTask 迁移到 AsyncTask?
      【解决方案8】:

      HandlerThread 可以用作 AsyncTask 的替代品。它们是长时间运行的线程。 HandlerThread的一个例子如下:

      您可以创建两个处理程序对象。其中之一将用于从 workerThread 向 UI Thread 发送消息。

      Handler uiHandler,workerHandler;
      Message msg;
      HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
      handlerThread.start();
      Handler.Callback callback=new Handler.Callback() {
              @Override
              public boolean handleMessage(@NonNull Message msg) {
                  // handle messages sent from working thread (like updating UI)...
                  return true;
              }
          }
      uiHandler=new Handler(callback);
      workerHandler = new Handler(handlerThread.getLooper());
      workerHandler.post(new Runnable(){
                 // Perform required task
                 uiHandler.sendMessage(msg); // this message will be sent to and handled by UI Thread
      });
      

      另外,请记住 HandlerThreads 在您的活动生命周期之外运行,因此需要正确清理它们,否则您将有线程泄漏。您可以在Activity的onDestroy()中使用quit()或quitSafely()方法来防止线程泄漏。

      【讨论】:

        【解决方案9】:

        你可以直接使用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
                        }
                    });
                }
            });
        

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

            ExecutorService executor = Executors.newSingleThreadExecutor();
            Handler handler = new Handler(Looper.getMainLooper());
        
            executor.execute(() -> {
                //Background work here
                handler.post(() -> {
                    //UI Thread work here
                });
            });
        

        尽管如此,它在代码简洁性方面无法击败 kotlin 条款,但比以前的 java 版本要好。

        希望这会对您有所帮助。谢谢你

        【讨论】:

        • 其实可以再往下走一步:Executors.newSingleThreadExecutor().execute(() -> dao.insert(data));
        • 谢谢你!它在调用服务器 API 调用时帮助了我很多。但是如果我想显示一些更新进度条怎么办?我可以把 onProgressUpdate 部分放在哪里?
        【解决方案10】:

        只要把整个类替换成这个Thread,放到一个方法中传递变量

        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();
        

        【讨论】:

          【解决方案11】:

          您可以使用此自定义类作为 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() {
          
              }
          });
          

          注意:别忘了关闭 executors 服务

          TaskRunner.getInstance().shutdownService();
          

          【讨论】:

            【解决方案12】:

            我的自定义替换:https://github.com/JohnyDaDeveloper/AndroidAsync

            它只在应用程序运行时工作(更具体地说是调度任务的活动),但它能够在后台任务完成后更新 UI

            编辑:我的 AsyncTask 不再需要 Activiy 才能运行。

            【讨论】:

              【解决方案13】:

              在这里,我使用 Coroutines 为 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)
                
                
                         }
                     }
                 }
                

              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();
              
              1. 如果您需要发送传递参数以防万一

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

              【讨论】:

              • 请不要使用 kotlin,首先使用 Java,然后也许 Kotlin 作为使用它的人的替代品。谢谢
              • @Darksymphony 我完全不同意你的观点,就使用 Java 而言,这个问题已经过时了。如果您仍在使用 Java for Android,则需要重新考虑您的选择。他为 AsynTask 写了一个非常好的替代方案。
              • 希望 Java 能在未来 X 年内继续作为 android 的基本语言。一个懒惰的人使用 Kotlin 并用它有趣的命令将它强加给 android 开发人员 :) 也许有一天我会重新考虑。但只要我们有选择,我会继续使用 Java
              • @Darksymphony 用 Kotlin 和 JAVA 编写一些部分的问题在哪里?应该可以正常工作。
              • @TheincredibleJan 是的,你是对的,但在不包括 Kotlin 依赖项的情况下也可以工作
              【解决方案14】:

              根据Android documentation AsyncTaskAPI 级别30 中已弃用,建议改用标准的java.util.concurrent 或Kotlin concurrency utilities .

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

              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
                 } 
                
              2. 使用任何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"
                          })
                      }
                  }
                  
                • 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"
                  })
                  

                要使用viewModelScopelifecycleScope 将下一行添加到应用的build.gradle 文件的依赖项:

                implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
                implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
                

                在撰写本文时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)
              }
              

              使用任何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
                  }
              )
              

              【讨论】:

              • 您对如何使用 kotlin 协程实现 onProgressUpdate 有什么建议吗?
              • 在 java 中有这个解决方案吗?
              • @Adnanhaider 恐怕不是。
              • @Undefinedfunction executeAsyncTaskCoroutineScope上的扩展函数,你可以创建自己的CoroutineScope并调用executeAsyncTask
              • 我尝试在 onPostExecute 中更新 UI,但我得到的是错误 ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.,它似乎没有在 UI 线程中运行
              【解决方案15】:

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

              那么,现在怎么样了?

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

              为什么是协程?

              协程是 Kotlin 进行异步编程的方式。编译器 自 Kotlin 1.3 起支持稳定,同时 kotlinx.coroutines图书馆-

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

              【讨论】:

              • 我听说 Coroutines 有“简单的入口”。这是否意味着它很容易被破坏?
              【解决方案16】:

              Google 建议使用 Java 的并发框架或 Kotlin 协程。但是 Rxjava 比 java 并发具有更多的灵活性和特性,因此获得了相当多的欢迎。

              【讨论】:

                【解决方案17】:
                private WeakReference<MyActivity> activityReference;
                

                很高兴它已被弃用,because the WeakReference&lt;Context&gt; was always a hack, and not a proper solution

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


                AsyncTask<String, Void, MyPojo> 
                

                根据这段代码,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);
                            });
                        });
                    }
                }
                

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

                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;
                    }
                }
                

                // 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);
                    }); 
                }
                

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

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

                【讨论】:

                • executor.post 出现错误。无法解析方法
                • @KrisB 显然它被称为 execute() 而不是 post()
                • newSingleThreadExecutor 更适合写入,但您绝对应该在文章末尾使用THREAD_POOL_EXECUTOR 进行数据库读取。
                • 我真的很欣赏这个例子。谢谢!我最终几乎完全按原样使用它。我使用了static 执行程序,就像您在最后的代码示例中显示的那样,但仍然使用了Executors.newSingleThreadExecutor()
                • 如果您确实需要取消,那么您必须使用 executor.submit 并取消未来,而不是 executor.execute。 baeldung.com/java-future
                猜你喜欢
                • 2022-05-02
                • 2019-07-05
                • 1970-01-01
                • 2020-09-09
                • 2011-03-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多