多次调用异步任务(AsyncTask)时出现的问题
2014-10-24 11:00
405 查看
最近在做一个项目,在一个Activity中多次调用AsyncTask时出现了问题:当启动一个AsyncTask并且cancel之后,再次启动AsyncTask就不成功。代码如下:
Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.
Note: this function schedules the task on a queue fora single background thread or pool of threads depending on the platform version. When first
introduced, AsyncTasks were executed serially on a single background thread. Starting with
this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting
tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the
however, see commentary there for warnings on its use.
This method must be invoked on the UI thread.
原来所有的异步任务都只在一个线程中执行。而cancel函数并不能把一个异步任务线程结束,通过打印信息可以发现cancel之后,doInBackground函数仍在执行:
那为什么不在更新UI呢:
Added in API level 3
This method can be invoked from
publish updates on the UI thread while the background computation is still running. Each call to this method will trigger the execution of
the UI thread.
not be called if the task has been canceled.
原来,执行cancel函数后,onProgressUpdate函数就不会再被执行。因此如果想继续调用AsyncTask,只能确保cancel之后,背景线程同时结束,更改代码。
这样就能够多次调用异步任务,而且不出错了。这时会发现每次调用异步任务,打印出来的线程ID不同,这就说明,在一个Activity中,同时只能有一个背景线程,只有当一个背景线程结束之后,才能启动一个新的背景线程。
-------------------更新-----------------------
在看Android Developer时发现了这么一句话:
Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.
Note: this function schedules the task on a queue fora single background thread or pool of threads depending on the platform version. When first introduced, AsyncTasks were executed
serially on a single background thread. Starting with
this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting
tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution.If you truly want parallel execution, you can use the
however, see commentary there for warnings on its use.
This method must be invoked on the UI thread.
也就是说使用executeOnExecutor函数能够同时启动多个背景线程,关键在于Executor的选择:
Added in API level 11
An
executes tasks one at a time in serial order. This serialization is global to a particular process.
同时只能启动一个背景任务线程,和execute()一样。
Added in API level 11
An
can be used to execute tasks in parallel.
可以并行执行多个任务,启动多个背景任务线程。
修改代码进行测试:
这样虽然也解决了之前所说的问题,但是多个异步任务存在很多隐患。因此还是用上一种方法比较稳妥。
public class MainActivity extends Activity implements OnClickListener{ private TextView tvHello; private Button btnStart,btnStop; private int Count; private CountTask ct; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.tvHello=(TextView)this.findViewById(R.id.tvHello); this.btnStart=(Button)this.findViewById(R.id.btnStart); this.btnStop=(Button)this.findViewById(R.id.btnStop); this.btnStart.setOnClickListener(this); this.btnStop.setOnClickListener(this); this.Count=0; } public class CountTask extends AsyncTask<Void, Void, Void>{ @Override protected Void doInBackground(Void... params) { // TODO Auto-generated method stub Log.e("AsyncTask", Long.toString(Thread.currentThread().getId())); while(true){ Log.e("Count",Integer.toString(Count)); this.publishProgress(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //return null; } @Override public void onProgressUpdate(Void... voids){ tvHello.setText(Integer.toString(Count++));//显示最新计数 } } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.btnStart: this.ct=new CountTask(); this.ct.execute(); break; case R.id.btnStop: if(this.ct!=null){ this.ct.cancel(true);//异步任务已启动的话,cancel之 } break; } } }去Android Developer上看了下:
Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.
Note: this function schedules the task on a queue fora single background thread or pool of threads depending on the platform version. When first
introduced, AsyncTasks were executed serially on a single background thread. Starting with
DONUT,
this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting
HONEYCOMB,
tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the
executeOnExecutor(Executor, Params...)version of this method with
THREAD_POOL_EXECUTOR;
however, see commentary there for warnings on its use.
This method must be invoked on the UI thread.
原来所有的异步任务都只在一个线程中执行。而cancel函数并不能把一个异步任务线程结束,通过打印信息可以发现cancel之后,doInBackground函数仍在执行:
那为什么不在更新UI呢:
protected final void publishProgress (Progress... values)
Added in API level 3This method can be invoked from
doInBackground(Params...)to
publish updates on the UI thread while the background computation is still running. Each call to this method will trigger the execution of
onProgressUpdate(Progress...)on
the UI thread.
onProgressUpdate(Progress...)will
not be called if the task has been canceled.
原来,执行cancel函数后,onProgressUpdate函数就不会再被执行。因此如果想继续调用AsyncTask,只能确保cancel之后,背景线程同时结束,更改代码。
protected Void doInBackground(Void... params) { // TODO Auto-generated method stub Log.e("AsyncTask", Long.toString(Thread.currentThread().getId())); <strong><span style="color:#ff0000;"> while(!this.isCancelled()){//如果cancel,则不再继续执行,线程结束</span></strong> Log.e("Count",Integer.toString(Count)); this.publishProgress(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; }
这样就能够多次调用异步任务,而且不出错了。这时会发现每次调用异步任务,打印出来的线程ID不同,这就说明,在一个Activity中,同时只能有一个背景线程,只有当一个背景线程结束之后,才能启动一个新的背景线程。
-------------------更新-----------------------
在看Android Developer时发现了这么一句话:
Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.
Note: this function schedules the task on a queue fora single background thread or pool of threads depending on the platform version. When first introduced, AsyncTasks were executed
serially on a single background thread. Starting with
DONUT,
this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting
HONEYCOMB a9fb,
tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution.If you truly want parallel execution, you can use the
executeOnExecutor(Executor, Params...)versionof this method with
THREAD_POOL_EXECUTOR;
however, see commentary there for warnings on its use.
This method must be invoked on the UI thread.
也就是说使用executeOnExecutor函数能够同时启动多个背景线程,关键在于Executor的选择:
public static final Executor SERIAL_EXECUTOR
Added in API level 11An
Executorthat
executes tasks one at a time in serial order. This serialization is global to a particular process.
同时只能启动一个背景任务线程,和execute()一样。
public static final Executor THREAD_POOL_EXECUTOR
Added in API level 11An
Executorthat
can be used to execute tasks in parallel.
可以并行执行多个任务,启动多个背景任务线程。
修改代码进行测试:
public class CountTask extends AsyncTask<Void, Void, Void>{ @Override protected Void doInBackground(Void... params) { // TODO Auto-generated method stub Log.e("AsyncTask", Long.toString(Thread.currentThread().getId())); while(true){//重新修改为True Log.e("Count",Integer.toString(Count)); this.publishProgress(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } @Override public void onProgressUpdate(Void... voids){ tvHello.setText(Integer.toString(Count++));//显示最新计数 } } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.btnStart: this.ct=new CountTask(); this.ct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);//允许执行并行任务 break; case R.id.btnStop: if(this.ct!=null){ this.ct.cancel(true);//异步任务已启动的话,cancel之 } break; } }这时,就会发现,cancel一个任务后,再启动,计数仍能进行。两个任务的背景线程都存在,相当于每启动一个任务就同时启动一个背景任务线程:
这样虽然也解决了之前所说的问题,但是多个异步任务存在很多隐患。因此还是用上一种方法比较稳妥。
相关文章推荐
- AsyncTask中各个函数详细的调用过程,初步实现异步任务
- QT线程QThread多次调用start()函数出现第2次调用失败问题备忘
- ajaxFileUpload异步上传资源,onchange多次调用问题的解决方案
- 关于linq to sql调用存储过程,出现"无法枚举查询结果多次"的问题
- AsyncTask中各个函数详细的调用过程,初步实现异步任务
- Task任务、多线程、异步调用返回值问题
- 关于view的drawRect多次被调用出现绘图重叠问题
- 异步任务AsyncTask的doInBackground执行慢的问题
- 关于linq to sql调用存储过程,出现"无法枚举查询结果多次"的问题
- 使用AsyncTask做异步任务以及BaseAdapter的getview方法的逻辑问题
- ASP.Net AJAX RC 调用Google Adsense代码出现的Sys.FormatException问题
- 解决BCB6调用MapX时放大出现invalid floating point operation的问题
- 故障解决:VS2005的水晶报表在WEB应用程序中多次使用后,就会出现加载报表失败.重启WEB服务器又正常了.过一段时间又出同样问题
- 主题:spring集成quartz,出现2次重复调用的问题
- 我用iret进行任务切换时,系统出现异常,来帮我看看是什么问题
- dll的输出函数使用__stdcall调用约定后,客户端用GetProcAddress出现的问题!
- .net 调用css/javascipt出现的问题及解决
- JBoss 4.0.5GA 多次部署应用程序出现 PermGen 内存不足(溢出)问题
- 使用异步委托解决Windows Application应用Duplex Service时出现的Deadlock问题
- 进入'我的电脑'窗口后,左边显示的‘系统任务,其它位置,详细信息’菜单条到删除和出现的问题解决