您的位置:首页 > 移动开发 > Android开发

多次调用异步任务(AsyncTask)时出现的问题

2014-10-24 11:00 405 查看
最近在做一个项目,在一个Activity中多次调用AsyncTask时出现了问题:当启动一个AsyncTask并且cancel之后,再次启动AsyncTask就不成功。代码如下:

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 3

This 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 11

An 
Executor
 that
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 11

An 
Executor
 that
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一个任务后,再启动,计数仍能进行。两个任务的背景线程都存在,相当于每启动一个任务就同时启动一个背景任务线程:



这样虽然也解决了之前所说的问题,但是多个异步任务存在很多隐患。因此还是用上一种方法比较稳妥。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android BUG 积累
相关文章推荐