20

回答完这个问题后,我对使用 Android 的 AsyncTask 类的 get() 方法的意义/有用性产生了疑问。

public final Result get ()

Waits if necessary for the computation to complete, and then retrieves its result. 

基本上,是 AsyncTask 类的同步解决方案,它阻塞(冻结)UI,直到后台操作完成。

除了测试目的之外,即使在那些情况下,我也无法真正想到它实际上是一个好的解决方案,但我可能错了,所以我感到好奇。

如果您需要用户真正等到 AsyncTask 完成,您可以显示一个 Dialog 或 ProgressDialog,随时控制 UI。我知道这并不完全相同,但恕我直言,这是一种比使用该方法更好的get()方法。

4

9 回答 9

18

AsyncTask不是进行后台操作的唯一方法。事实上,文档说这AsyncTask应该只用于操作最多需要几秒钟。因此,如果您的任务需要更长的时间,则应通过实现可运行接口的类对其进行编码。其他(非 AsyncTask)线程中的此类任务很可能希望等待 anAsyncTask完成,因此在我看来,没有人想要使用的情况的想法AsyncTask.get()是错误的。

更新:作为对评论的回应,为了强调这可能是对 的有效使用AsyncTask.get(),以下是可能的:

  • 可能有AsyncTask从 UI 线程启动的 s,这可能涉及通过 Internet 进行通信,例如加载网页或与服务器通信。无论结果如何,AsyncTask都需要部分(或全部)结果来更新屏幕。因此,在 UI 线程上AsyncTask使用doInBackground其后跟onPostExecute是有意义的。
  • 每当 UI 线程启动AsyncTask时,它会将AsyncTask对象放入队列中,以便在结果可用后由单独的后台线程进行额外处理。
  • 对于AsyncTask队列中的每一个,后台线程用于AsyncTask.get()等待任务完成,然后再进行额外的处理。附加处理的一个明显示例可能只是将所有此类AsyncTask活动记录到 Internet 上的服务器,因此在后台执行此操作是有意义的。
下面的代码说明了我的意思。KickOffAsynctask(...)该应用程序在它想要​​执行 AsyncTask 时调用,并且有一个后台线程会在任务完成后自动拾取任务进行后处理。

public class MyActivity extends Activity {

    static class MyAsyncTaskParameters { }
    static class MyAsyncTaskResults { }

    Queue<MyAsyncTask> queue;   // task queue for post-processing of AsyncTasks in the background
    BackgroundThread b_thread;  // class related to the background thread that does the post-processing of AsyncTasks

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
        queue = new  ConcurrentLinkedQueue<MyAsyncTask>();
        b_thread = new BackgroundThread(queue);
        b_thread.Start();       
    }

    void KickOffAsynctask(MyAsyncTaskParameters params) {
        MyAsyncTask newtask = new MyAsyncTask();
        newtask.execute(params);
        synchronized(queue) {
            queue.add(newtask);
        }
    }

    static class MyAsyncTask extends AsyncTask<MyAsyncTaskParameters, Void, MyAsyncTaskResults> {

        @Override
        protected MyAsyncTaskResults doInBackground(MyAsyncTaskParameters... params) {
            MyAsyncTaskResults results = new MyAsyncTaskResults();
            // do AsyncTask in background
            return results;
        }

        @Override
        protected void onPostExecute(MyAsyncTaskResults res){
            // take required results from MyAsyncResults for use in the user interface
        }

    }

    static class BackgroundThread implements Runnable {
        // class that controls the post processing of AsyncTask results in background

        private Queue<MyAsyncTask> queue;
        private Thread thisthread;
        public boolean continue_running;

        public BackgroundThread(Queue<MyAsyncTask> queue) {
            this.queue=queue; thisthread = null; continue_running = true;
        }

        public void Start() {
            thisthread = new Thread(this);
            thisthread.start();
        }

        @Override
        public void run() {
            try {
                do {
                    MyAsyncTask task;
                    synchronized(queue) {
                        task = queue.poll();
                    }
                    if (task == null) {
                        Thread.sleep(100);
                    } else {
                        MyAsyncTaskResults results = task.get();
                        // post processing of AsyncTask results in background, e.g. log to a server somewhere
                    }
                } while (continue_running);     
            } catch(Throwable e) {
                e.printStackTrace();
            }           
        }

    }

}

更新2。我想到了另一个可能的有效用法AsyncTask.get()。标准建议不要AsyncTask.get()从 UI 线程使用,因为它会导致用户界面冻结,直到结果可用。但是,对于需要隐身的应用程序,这可能正是所需要的。那么下面的情况如何:詹姆斯邦德闯入Le Chiffre的酒店房间,只有几分钟的时间从恶棍的手机中提取所有数据并安装监控病毒。他安装了Q提供的应用程序并开始运行,但他听到有人来了,所以他不得不躲起来。 雪佛兰进了房间,拿起手机打电话。几秒钟后,手机似乎有点没反应,但突然手机醒来,他没有多想就拨通了电话。当然,无响应的原因是Q的应用程序正在运行。它有各种各样的任务要做,其中一些需要按照特定的顺序完成。该应用程序使用两个线程来完成这项工作,即 UI 线程本身和处理AsyncTasks 的单个后台线程。UI 线程完全控制所有任务,但由于某些任务需要在其他任务之前完成,因此在应用程序中存在一些点,UI 线程AsyncTask.get()在等待后台任务完成时使用 :-)。

于 2013-06-11T19:39:09.447 回答
5

get方法不应该在 UI 线程中使用,因为它会(如您所见)冻结您的 UI。它应该用在后台线程中等待你的任务结束,但一般来说,尽量避免AsyncTask为什么?)。

我建议使用回调或事件总线作为替代。

于 2013-06-04T08:19:27.173 回答
5

如果您并行运行多个 asyncTask,您可能希望某些任务等待其他任务的结果。

但是,我认为你应该避免并行运行太多的 asyncTask,因为它会减慢设备的速度。

于 2013-06-04T08:39:50.697 回答
4

我通常使用处理程序类来解决问题(加载媒体异步)。喜欢:

public void setRightSongAdele(Song current)
{
    RetriveCorrelateGenreSong rcgs = new RetriveCorrelateGenreSong(current);
    new Thread(rcgs).start();
}

@SuppressLint("HandlerLeak")
Handler updateRightAdeleHandler = new Handler()
{
    @Override
     public void handleMessage(Message msg) {
        songNextText.setText(utils.reduceStringLength(rightSong.getTitle(), 15));
        adeleNext.setImageBitmap(utils.getAlbumArtFromSong(rightSong.getPath(), getApplicationContext()));
    }
};
于 2013-06-04T08:47:04.140 回答
3

我发布我自己的答案,因为这是我此刻的想法,没有人给出它。

我的好奇心还活着,所以我不会将这个标记为正确的。如果有人出色地回答了这个问题,我会将他/她的答案标记为正确。

我的回答是,实际上不存在使用 AsyncTask 的get()方法是最佳解决方案的情况。

恕我直言,使用这种方法毫无意义。我的意思是,似乎总有更好的解决方案。

同样,我会说它在语义上令人震惊,因为调用它会将您的 AsyncTask 更改为有效的“SyncTask”。

于 2013-06-04T21:13:23.187 回答
2

正如文档所代表的那样:

“如有必要,等待计算完成,然后检索其结果。”

所以你必须避免在 UI 线程上调用它,否则你会得到NetworkOnMainThreadException. 我会在涉及网络操作或等待另一个任务完成的测试环境中使用它。

编辑:我错了NetworkOnMainThreadException

于 2013-06-06T09:16:12.010 回答
2

看起来好像 AsyncTask.get() 阻塞了调用者线程,而 AsyncTask.execute() 没有。

您可能希望将 AsyncTask.get() 用于要测试特定 Web 服务调用的测试用例,但您不需要它是异步的,并且您希望控制完成所需的时间。或者任何时候您想在测试套件中针对您的 Web 服务进行测试。

语法与执行相同:

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
    }

new DownloadFilesTask().get(5000, TimeUnit.MILLISECONDS);
于 2013-06-13T05:19:38.227 回答
0

您可以在 AutoCompleteTextView 的适配器过滤器中使用它。

private class Adapter extends BaseAdapter implements Filterable{
    getFilter(){
        return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            List<String> suggestions = MyApiSuggestionsApi(constraint).get();
            return packageAsFilterResults(suggestions);
        }
    }
}
于 2014-03-14T01:04:31.680 回答
-1

我有一个编辑照片的应用程序,用户能够从 URL 打开照片。

我使用 AsyncTask 从 URL 下载照片,在此期间 UI 应该被阻止。我所做的是我展示了一个 ProgressBar 启动 AsyncTask 的 get() 方法而不是隐藏 ProgressBar ..

对我来说,这似乎是最好和最简单的解决方案。

于 2013-06-13T06:53:30.887 回答