首先,我得解释一下为什么我的标题取消异步加载打引号,这是因为可能最后实现效果并不是你自己想象中的那样。大家看取消异步加载,这不是很简单吗?AsyncTask中不是有一个cancel方法吗?直接调用该方法不就行了吗?但是事实上是这样的吗?如果真是这样,我相信我就没有以写这个作为一篇博客的必要了。为什么会有这样的想法呢?实际上源于我上一篇中Demo中的一个BUG,然后解决该BUG,需要去取消异步任务,是怎么样,我们不妨来看看。
首先,还是来一起回顾一下上篇博客中加载进度条Demo吧。
AsyncTask子类:
- 01.package com.mikyou.utils;
- 02.
- 03.import android.os.AsyncTask;
- 04.import android.widget.ProgressBar;
- 05.import android.widget.TextView;
- 06.
- 07.public class MikyouAsyncTaskProgressBarUtils extends AsyncTask<Void, Integer, String>{
- 08. private TextView tv;
- 09. private ProgressBar bar;
- 10. public MikyouAsyncTaskProgressBarUtils(TextView tv,ProgressBar bar){
- 11. this.bar=bar;
- 12. this.tv=tv;
- 13.
- 14. }
- 15. @Override
- 16. protected String doInBackground(Void... params) {
- 17. for(int i=1;i<101;i++){
- 18. try {
- 19. Thread.sleep(1000);
- 20. } catch (InterruptedException e) {
- 21. e.printStackTrace();
- 22. }
- 23. publishProgress(i);
- 24. }
- 25. return "下载完成";
- 26. }
- 27. @Override
- 28. protected void onProgressUpdate(Integer... values) {
- 29. bar.setProgress(values[0]);
- 30. tv.setText("下载进度:"+values[0]+"%");
- 31. super.onProgressUpdate(values);
- 32. }
- 33. @Override
- 34. protected void onPostExecute(String result) {
- 35. tv.setText(result);
- 36. super.onPostExecute(result);
- 37. }
- 38.}
MainActivity
- 01.package com.mikyou.asynctask;
- 02.
- 03.import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;
- 04.
- 05.import android.app.Activity;
- 06.import android.os.Bundle;
- 07.import android.view.View;
- 08.import android.widget.Button;
- 09.import android.widget.ProgressBar;
- 10.import android.widget.TextView;
- 11.
- 12.public class MainActivity extends Activity {
- 13. private Button downLoad;
- 14. private ProgressBar bar;
- 15. private TextView tv;
- 16. @Override
- 17. protected void onCreate(Bundle savedInstanceState) {
- 18. super.onCreate(savedInstanceState);
- 19. setContentView(R.layout.activity_main);
- 20. initView();
- 21. }
- 22. private void initView() {
- 23. bar=(ProgressBar) findViewById(R.id.bar);
- 24. tv=(TextView) findViewById(R.id.tv);
- 25. }
- 26. public void download(View view){
- 27. MikyouAsyncTaskProgressBarUtils mikyouAsyncTaskProgressBarUtils=new MikyouAsyncTaskProgressBarUtils(tv, bar);
- 28. mikyouAsyncTaskProgressBarUtils.execute();
- 29. }
- 30.
- 31.}
运行结果的Demo的BUG:
通过分析上面的demo会发现:
当我们点击开始下载后,进度条就开始更新了,然后就在更新中途退出Activity,然后再次进入Activity,点击开始下载会发现,等了好一会,进度条才开始更新。
原因:这并不是本程序的一个BUG,而是异步加载的一个机制,因为异步加载从源码中我们可以得出,它底层的实现还是Thread或者Thread-pool+Handler机制
那么它的机制就是如果当前开始的线程没有执行完毕,其他的线程必须等待,这也就解释了为什么说异步加载是安全,因为可以基于该机制避免了线程同步带来的安全问题。
解决办法:其实很简单的就是将异步加载的生命周期和我们的Activity的生命周期进行绑定,当我第一次在中途中断的时候,退出Activity时会调用OnPause
方法,只需要在OnPause方法中顺便把本次中断的异步任务取消即可,也即把当前的线程池中的线程给取消了,当下次重新进入的时候,线程池中并没有
其他的子线程,那么它就无需等待其他的线程了,直接运行。
通过以上的分析,我们就可以得到了一个解决办法,那就是如何去取消当前的异步任务,那就我们就按照大家的一致想法使用cancel方法来终止异步任务。
来了看看是否可以达到我们想要的效果呢??一起来看看
- package com.mikyou.asynctask;
-
- import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;
-
- import android.app.Activity;
- import android.os.AsyncTask;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ProgressBar;
- import android.widget.TextView;
-
- public class MainActivity extends Activity {
- private Button downLoad;
- private ProgressBar bar;
- private TextView tv;
- private MikyouAsyncTaskProgressBarUtils mTask;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initView();
- }
- private void initView() {
- bar=(ProgressBar) findViewById(R.id.bar);
- tv=(TextView) findViewById(R.id.tv);
- }
- public void download(View view){
- mTask=new MikyouAsyncTaskProgressBarUtils(tv, bar);
- mTask.execute();
- }
-
- @Override
- protected void onPause() {
-
- if (mTask!=null&&mTask.getStatus()==AsyncTask.Status.RUNNING) {
-
-
-
-
- mTask.cancel(true);
-
- }
- super.onPause();
- }
- }
运行结果:
大家仔细看下,发现好像我们加的cancel方法并没有什么用,这也就我这篇需要讲的了,
为什么无法去停止一个异步任务,这是为什么呢?
注意:这是因为cancel方法只是发出一个请求取消异步任务的信号, 将对应当前的异步任务标记为CANCEL状态,而并不是真正取消线程的执行,而此时异步任务中的线程仍然在执行并没有结束,所以效果依然是这样的,并且在Java中我们是无法直接暴力将一个线程给停止掉 既然我们知道无法去取消一个已经正在运行的线程,但是我们如何去解决这个BUG呢?在异步任务中还给我们提供一个isCanceled的回调方法,也就是当我已经给当前的异步任,调用了cancel(true)方法,发出一个请求取消异步任务的信号,那么此时的isCanceled的回调方法会直接返回一个true,那么我们就可以通过判断当前异步任务isCanceled是否为true,来终止线程中的操作而不是去终止线程,从而达到了界面显示好像线程中的操作被终止了,而实际上该线程依然在运行
因为我们是无法去彻彻底底地采用暴力的方法直接kill一个线程,所以我们不能直接去取消一个异步任务,但是我们可以通过调用cancel方法来发送一个取消异步任务的请求信号,这时候就会给mCanceled的值设置为true,标记为该异步任务是取消了,通过回调方法isCanceled来返回一个true,表示该异步任务取消。如果有疑问我们来看下cancel反方法的源码就知道了。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public final boolean cancel(boolean mayInterruptIfRunning) {
- mCancelled.set(true);
- return mFuture.cancel(mayInterruptIfRunning);
- }
会看mCanceled会设置为true,该值将会通过isCanceled方法回调传出去。所以,我们采用这样一个想法,既然我们不能去终止一个线程,那么我们可以间接解决这个bug
通过判断该值直接终止线程中的操作,而不是去终止线程。
- package com.mikyou.asynctask;
-
- import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;
-
- import android.app.Activity;
- import android.os.AsyncTask;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ProgressBar;
- import android.widget.TextView;
-
- public class MainActivity extends Activity {
- private Button downLoad;
- private ProgressBar bar;
- private TextView tv;
- private MikyouAsyncTaskProgressBarUtils mTask;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initView();
- }
- private void initView() {
- bar=(ProgressBar) findViewById(R.id.bar);
- tv=(TextView) findViewById(R.id.tv);
- }
- public void download(View view){
- mTask=new MikyouAsyncTaskProgressBarUtils(tv, bar);
- mTask.execute();
- }
-
- @Override
- protected void onPause() {
-
- if (mTask!=null&&mTask.getStatus()==AsyncTask.Status.RUNNING) {
-
-
-
-
- mTask.cancel(true);
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- super.onPause();
- }
- }
AsyncTask的子类
- package com.mikyou.utils;
-
- import android.os.AsyncTask;
- import android.widget.ProgressBar;
- import android.widget.TextView;
-
- public class MikyouAsyncTaskProgressBarUtils extends AsyncTask<Void, Integer, String>{
- private TextView tv;
- private ProgressBar bar;
- public MikyouAsyncTaskProgressBarUtils(TextView tv,ProgressBar bar){
- this.bar=bar;
- this.tv=tv;
-
- }
- @Override
- protected String doInBackground(Void... params) {
-
- for(int i=1;i<101;i++){
- try {
- if (isCancelled()) {
- break;
- }
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- publishProgress(i);
- }
- return "下载完成";
- }
- @Override
- protected void onProgressUpdate(Integer... values) {
- if (isCancelled()) {
- return ;
- }
- bar.setProgress(values[0]);
- tv.setText("下载进度:"+values[0]+"%");
- super.onProgressUpdate(values);
- }
- @Override
- protected void onPostExecute(String result) {
- tv.setText(result);
- super.onPostExecute(result);
- }
- }
运行结果: