如何为不同的AsyncTasks分配不同的线程,因此它们不会在一行中等待彼此

时间:2015-04-16 13:30:28

标签: android multithreading android-asynctask

在我的应用程序中,当用户点击"保存产品"在AsyncTask中启动了一长串网络操作。

同时,如果用户打开导航抽屉并单击菜单项,则使用另一个AsyncTask,以便加载数据不会阻止UI,这样我就可以防止导航抽屉以一种活泼的方式关闭,而是顺利。

问题是第二个AsyncTask显然等待第一个完成,然后它打开新选择的菜单项。在用户点击它几秒钟后,导航抽屉保持打开状态。

当我将要从AsyncTask打开的菜单项的加载数据切换到主线程时,导航抽屉立即关闭(但是滞后/时髦,不知道什么是更好的词)

那么如何将这个AsyncTask(关闭导航抽屉的那个)分配给另一个线程,这样它就不会等到第一个完成它才能启动?或者有没有办法指导两者并行运行?

3 个答案:

答案 0 :(得分:3)

来自Android文档:

  

执行顺序

     

首次引入时,AsyncTasks在单个后台线程上串行执行。从DONUT开始,这被改为一个线程池,允许多个任务并行运行。从HONEYCOMB开始,任务在单个线程上执行,以避免由并行执行引起的常见应用程序错误。

     

如果您真的想要并行执行,可以使用THREAD_POOL_EXECUTOR调用executeOnExecutor(java.util.concurrent.Executor,Object [])。

http://developer.android.com/reference/android/os/AsyncTask.html

通常,如果您想要执行大量线程或长时间运行操作,则不建议使用AyncTasks。您最好使用ThreadPoolExecutorScheduledThreadPoolExecutor

答案 1 :(得分:1)

正如Tim B建议的那样,THREAD_POOL_EXECUTOR会为你做到这一点:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
} else {
    asyncTask.execute(params);
}

答案 2 :(得分:1)

我的建议是使用AsyncTaskLoader而不是AsyncTask。

AsyncTasks有几个问题:

  • 无论Activity / Fragment生命周期如何,AsyncTask都会运行。暂停活动不会暂停AsyncTask,因此在活动之间导航可能会留下运行AsyncTasks的“痕迹”(对性能,电池和响应性不利)。
  • 配置更改(尤其是方向更改)是有问题的,因为AsyncTask无法在更改后更新ui(Activity可能已被销毁)并且新创建的Activity无法“查找”已经启动的也许还在运行AsyncTask。
  • AsyncTasks让IMO成为一个主要的设计缺陷,因为它们进行后台处理并更新ui(在onPostExecute中)。 ui是Activity / Fragment及其生命周期的一部分,只有那些应该可以访问ui元素并能够修改/更新它们。通过将ui更新委托给独立于ui / Activity生命周期的组件(AsyncTask),将不可避免地遇到问题。

使用Loaders(http://developer.android.com/reference/android/content/AsyncTaskLoader.html),所有这些问题都已过时。 IMO的主要区别在于它们只进行后台处理,并在完成后报告回来让Activity / Fragment执行ui部分。它们还可以优雅地处理配置更改,这意味着ui部分(活动/片段)和Loader可以在这样的更改后“重新连接”,无需在屏幕旋转后启动新的Loader。一些实现(即CursorLoader)也能够监视对底层数据的更改并自动重新查询(尽管不太相关)。

最后但并非最不重要(回答原始问题),启动新的AsyncTaskLoader永远不会阻止正在运行的线程:

LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(LOADER_ID, null, this);

initLoader是一个非阻塞调用,并且将始终立即返回。

以下是AsyncTaskLoader的最基本示例:

LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks<Result>() {
    @Override
    public Loader<Result> onCreateLoader(int id, Bundle args) {
        return new BackgroundTask(maybe some parameters...);
    }

    @Override
    public void onLoaderReset(Loader<Result> loader) {}

    @Override
    public void onLoadFinished(Loader<Result> loader, Result data) {
        // here we update the ui
    }
}

class BackgroundTask extends AsyncTaskLoader<Result> {
    public BackgroundTask(Context context) {
        super(context);
    }

    @Override
    public Result loadInBackground() {
        // do the background processing
    }
}

虽然这看起来像很多样板代码,但它非常简单,并且很好地显示了后台处理(在BackgroundTask中)和ui部分的分离。

我知道OP可能只是寻找快速修复,其他答案提供了这一点。另一方面,AsyncTaskLoader可能是长期发展的方式。

http://developer.android.com/reference/android/content/AsyncTaskLoader.html