2012-08-28 72 views
27

我有2个ASyncTasks,一个从httpPost获取一个值,另一个更新UI的一些元素(包括一个列表视图)。 问题是,由于两个ASyncTasks共享相同的后台线程,如果网络操作首先启动并运行缓慢(由于网络连接不良)。其他后台线程花费太多时间让应用程序不负责任。阻止他人的异步任务

由于两个ASyncTasks都是独立的,所以非常愚蠢,一个要等待另一个。这将是更合乎逻辑的asynctasks不同的类使用不同的线程,我错了吗?

阅读ASyncTask文档。谈论使用executeOnExecutor(),但我如何解决在API级别低于11?

这里去再现 “问题”

 new Task1().execute(); 
     new Task2().execute(); 

用一个小例子

public class Task1 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 1"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 1"); 
     return null; 
    } 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 1"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 1"); 
     super.onPostExecute(result); 
    } 

} 

public class Task2 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 2"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 2"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 2"); 
     return null; 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 2"); 
     super.onPostExecute(result); 
    } 

} 
+0

为什么你有一个AsyncTask来更新UI?您需要在Ii线程上同步执行此操作。你的UI AsyncTask在后台线程上做了些什么吗? –

+0

我用它来显示新闻的列表视图。在onPreExecute中,显示loading()UI,在后台从Internet中检索新闻,并在onPostExecute中将适配器分配给listview。这种方法不正确吗? – Addev

+0

@Addev:你为什么不直接在第一个'onPostExecute(...)'方法中启动第二个'AsyncTask'?如果你需要的是'AsyncTask'依赖另一个的结果,那么你实际上有一个同步需求,你不应该并行运行两个异步操作。 – Squonk

回答

46

这是我在我的代码处理这个问题:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
    new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
} else { 
    new MyAsyncTask().execute(); 
} 

而更换MyAsyncTask分别与您的Task1Task2。基本上,在Honeycomb中出现了AsyncTask的变化(请参阅“执行顺序”一节中的Android SDK文档here),因此在此之前,如果您不喜欢新行为(没有人会这样做,您可以像往常一样启动它,对于HC和更高版本,请使用executeOnExecutor())我认为)

7

一个做到这一点稍微更普遍的方式是把两个辅助方法,一个工具类,像这样:

class Utils { 

    @SuppressLint("NewApi") 
    public static <P, T extends AsyncTask<P, ?, ?>> void execute(T task, P... params) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
      task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
     } else { 
      task.execute(params); 
     } 
    } 
} 

然后你就可以用Utils.execute(mytask)Utils.execute(mytask, params)执行任务,它会照顾并行执行它们。

+0

谢谢。注意后人,不要试图重写'AsyncTask.execute()' - 你不能;这是'最终'。 – TalkLittle

+0

另外第一种方法是不必要的,可能会导致意外的行为('params'可以是'null'而不是空数组)。在预蜂窝设备上移除它可以很好地编译/运行。 – TalkLittle

+0

这不是没有必要,它可以节省打字:) –

0

问题是每个AsyncTask都运行在编码到API中的同一个ThreadPoolExecutor中。这个ThreadPoolExecutor可以根据你的Android版本创建不同数量的WorkerThread。我不记得数字版本,但想法是在较旧的Android版本中它是1 WorkerThread。然后在更高版本中将其更新为5。最近又回到了1。这就是您的AsyncTasks被阻止的原因。它们在同一个WorkerThread上运行,因此它们按顺序执行。尝试使用带有THREAD_POOL_EXECUTOR的executeOnExecutor来实现真正的并行执行。但是,只有API 11以后才能使用它。