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

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

这样虽然也解决了之前所说的问题,但是多个异步任务存在很多隐患。因此还是用上一种方法比较稳妥。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值