AsyncTask使用及解析
阅读原文时间:2021年04月20日阅读:4

目录

1.AsyncTask介绍

1)AsyncTask抽象类的3参数

2)继承AsyncTask可以实现的函数

3)常用公共函数

2.AsyncTask使用

1)demo1:加载单张图片

2)demo2:异步加载多张图片

3)取消异步任务

3.注意点

1)AsyncTask不与任何组件绑定生命周期

2)内存泄漏

3)屏幕旋转

4)串行或者并行的执行异步任务

4.源码分析


1.AsyncTask介绍

AsyncTask可以用来处理一些后台较耗时的任务,查看源码发现其内部就是一个Handler和线程池的封装,可以帮助我们处理耗时任务的同时去更新UI。

1)AsyncTask抽象类的3参数

public abstract class AsyncTask<Params, Progress, Result> {
......
}
  •  Params 启动任务执行的输入参数,比如下载URL
  •  Progress 后台任务执行的百分比,比如下载进度
  •  Result 后台执行任务最终返回的结果,比如下载结果

2)继承AsyncTask可以实现的函数

1. onPreExecute():(运行在UI线程中) (非必须方法,可以不用实现)

在任务没被线程池执行之前调用,通常用来做一些准备操作,比如下载文件任务执行前,在这个方法中显示一个进度条。

2.doInBackground(Params… params):(运行在子线程中)(此函数是抽象函数必须实现)

在任务被线程池执行时调用 ,可以在此方法中处理比较耗时的操作,比如下载文件等等。

3.onProgressUpdate(Progress… values) (运行在UI线程中) (非必须方法,可以不用实现)

此函数是任务在线程池中执行处于Running状态,回调给UI主线程的进度,比如上传或者下载进度。

4.onPostExecute(Result result)(运行在UI线程中) (非必须方法,可以不用实现)

此函数代表任务在线程池中执行结束了,回调给UI主线程的结果。比如下载结果。

5.onCancelled(Result result)onCancelled()任务关闭的函数

3)常用公共函数

1.cancel (boolean mayInterruptIfRunning)尝试取消这个任务的执行(如果这个任务已经结束或者已经取消或者不能被取消或者某些其他原因,那么将导致这个操作失败)

2.execute (Params… params)用指定的参数来执行此任务(此方法必须在UI线程中调用,因为要将handler绑定到主线程的Looper,方便后台任务执行完毕后,由后台线程切换到UI线程去更新UI)。

3.executeOnExecutor(Executor exec,Params… params)用指定的参数行此任务,并运行在指定的线程池中(此方法必须在UI线程中调用)

4.getStatus ()获得任务的当前状态  PENDING(等待执行)、RUNNING(正在运行)、FINISHED(运行完成)

5.isCancelled ()在任务正常结束之前能成功取消任务则返回true,否则返回false

2.AsyncTask使用

 1)demo1:加载单张图片

public class ImageText extends Activity {

    ImageView imageView;
    ProgressBar progressBar;
    String URL = "http://b.zol-img.com.cn/sjbizhi/images/4/320x510/1366618073898.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.imagetext);
        imageView = (ImageView) findViewById(R.id.imageview_imagetext);
        progressBar = (ProgressBar) findViewById(R.id.progress_imagetext);
        //开始异步加载图片——传入图片的URL
        new MyAsyncTask().execute(URL);  
    }


 //3泛型:url类型,void(不要进度),返回值类型
 class  MyAsyncTask extends AsyncTask<String,Void,Bitmap>{ 

          //第一阶段————准备阶段让进度条显示
          @Override
          protected void onPreExecute() {
              super.onPreExecute();
              progressBar.setVisibility(View.VISIBLE);  
          }     

          //第二阶段——网络获取图片
          @Override
          protected Bitmap doInBackground(String... params) {
              //从可变参数的数组中拿到第0位的图片地址
              String newurl = params[0]; 
              Bitmap bitmap = null;
              URLConnection urlConnection;
              InputStream  is = null;
              //图片下载操作
              try {
                  urlConnection = new URL(newurl).openConnection(); 
                  is = urlConnection.getInputStream();  
                  BufferedInputStream bf=new BufferedInputStream(is);
                  //图片加载的太快,可以用线程睡眠
                  Thread.sleep(1000);
                  bitmap = BitmapFactory.decodeStream(bf);  
                  is.close(); 
                  bf.close();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              //返回结果bitmap
              return bitmap; 
          }

          //第三阶段,拿到结果bitmap图片,更新ui
          @Override
          protected void onPostExecute(Bitmap bitmap) {  
              super.onPostExecute(bitmap);
              progressBar.setVisibility(View.GONE); 
              imageView.setImageBitmap(bitmap);     
          }
      }
}

2)demo2:异步加载多张图片

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;
    private String imgUrls[] = {"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=922669495,273204025&fm=80&w=179&h=119&img.JPEG",
            "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=922669495,273204025&fm=80&w=179&h=119&img.JPEG"};
    private List<Bitmap> list = new ArrayList();
    private Bitmap bitmap = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = (ProgressBar) findViewById(R.id.pro);
        new MyAsyncTask().execute(imgUrls);
    }

    class MyAsyncTask extends AsyncTask<String, Void, List<Bitmap>> {
        //第一阶段————准备阶段让进度条显示
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressBar.setVisibility(View.VISIBLE);
        }

        //第二阶段——网络获取图片——返回类型为list集合
        //for循环遍历可变参数,拿到单次任务所需的图片路径
        @Override
        protected List<Bitmap> doInBackground(String... params) {
            for (int i = 0; i < params.length; i++) {
                //从可变参数的数组中拿到图片地址
                String newurl = params[i];
                URLConnection urlConnection;
                InputStream is = null;
                try {
                    urlConnection = new URL(newurl).openConnection();
                    is = urlConnection.getInputStream();
                    BufferedInputStream bf = new BufferedInputStream(is);
                    bitmap = BitmapFactory.decodeStream(bf);
                    list.add(bitmap);
                    is.close();
                    bf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //返回存bitmap的集合结果给onPostExecute方法
            return list;
        }

        //第三阶段———拿到结果数据,更新ui
        @Override
        protected void onPostExecute(List<Bitmap> list) {
            super.onPostExecute(list);
            progressBar.setVisibility(View.GONE);
        }
    }
}

3)取消异步任务

public class MainActivity extends AppCompatActivity {

    ProgressBar progressBar;
    MyAsyncTask mytask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = (ProgressBar) findViewById(R.id.pro);
        //开始异步
        mytask = new MyAsyncTask();
        mytask.execute();
    }

    //将AsyncTask和activity生命周期绑定-暂停状态时候去终止异步操作
    @Override
    protected void onPause() {
        super.onPause();
        //运行状态
        if (mytask != null && mytask.getStatus() == AsyncTask.Status.RUNNING) {
            //cancel方法只是将对应的AsyncTask标记为cancel状态,并没有真正的取消线程的执行。
            mytask.cancel(true);
        }
    }

    //3泛型参数:void,进度条int值,void
    class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
        //任务执行逻辑:
        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i <= 100; i++) {
                //判断是否为cancel的状态,是就直接break
                if (isCancelled()) {
                    break;
                }
                //更新进度条进度需要使用的方法-绑定onProgressUpdate方法
                publishProgress(i);
                try {
                    //模拟进度条走动的效果
                    Thread.sleep(400);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        //拿到实时更新的进度值
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //判断是否为cancel的状态
            if (isCancelled()) {  
                return;
            }
            progressBar.setProgress(values[0]);
        }
    }
}

上面AsyncTask.cancel方法只是将对应的AsyncTask标记为cancel状态,并没有真正的取消线程的执行。会在任务执行时候判断是cancel状态,才会终止任务的执行。同时可以看到进度条值改变,更新值的方法为publishProgress(i),会将进度值传递给onProgressUpdate()方法,从而更新UI。

3.注意点

1)AsyncTask不与任何组件绑定生命周期

在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(true);

2)内存泄漏

如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

3)屏幕旋转

屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

4)串行或者并行的执行异步任务

        目前AsyncTask支持并行和串行的执行异步任务,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)。

        3.0后新增了一个方法executeOnExecutor(Executor exec, Object… params)。第一个是Executor(线程池实例),第二个是任务参数

public class MainActivity extends AppCompatActivity {

    AsyncTask<String[], Void, Void> mytask;
    String imgUrls[] = {"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=922669495,273204025&fm=80&w=179&h=119&img.JPEG",
            "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=922669495,273204025&fm=80&w=179&h=119&img.JPEG"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //开始异步
        mytask = new MyAsyncTask().executeOnExecutor(Executors.newFixedThreadPool(1),imgUrls);
    }

    class MyAsyncTask extends AsyncTask<String[], Void, Void> {
        @Override
        protected Void doInBackground(String[]... params) {
            return null;
        }
    }
}

Executor主要由四种类型:

newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若不可回收,则新建线程

newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

newScheduledThreadPool:建一个定长线程池,支持定时及周期性任务执行

newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行

创建线程池方式为:

asyncTask.executeOnExecutor(Executors.newFixedThreadPool(1), params)

asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor, params)

4.源码分析

先看看构造函数:

public abstract class AsyncTask<Params, Progress, Result> { 

  private final WorkerRunnable<Params, Result> mWorker;
  private final FutureTask<Result> mFuture;

   /**
    *创建一个新的异步任务。这个构造器必须在UI线程上调用。
    */
    public AsyncTask() {
        this((Looper) null);
    }

    /**
     *创建一个新的异步任务。这个构造器必须在UI线程上调用。
     *@hide---不可以直接调用
     */
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    /**
     * 创建一个新的异步任务。这个构造器必须在UI线程上调用。
     * @hide---不可以直接调用
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        //实例化handler
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        //单个任务执行的工作线程WorkerRunnable
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //设置为true,代表当前方法被调用过
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //设置线程的优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

                    //将任务参数传递给doInBackground方法处理
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //处理任务结果,更新UI
                    postResult(result);
                }
                return result;
            }
        };


        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
}

可以看到实例化AsyncTask同时,内部也实例化了带Looper的Handler,为UI更新做好准备。WorkRunnable可以理解为一个工作线程,同时自身实现了CallBack接口,Call方法中包含了AyncTask最终要执行的任务,

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
}

看看Callback的解释:得到一个任务执行完后的结果,或者如果不能这样做,就抛出异常。

public interface Callable<V> {
    //得到一个任务执行完后的结果,或者如果不能这样做,就抛出异常。
    V call() throws Exception;
}

目光再回到AsyncTask构造方法,WorkRunnable实例化同时,内部通过doInBackground(mParams)拿到任务参数去执行任务,最终将返回结果result调用postResult()方法。

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
}

postResult就是将Result携带的信息,发送给指定target的Handler。

(题外话:在Java中一般通过继承Thread类或者实现Runnable接口这两种方式来创建多线程,但是这两种方式都有个缺陷,就是不能在执行完成后获取执行的结果,因此Java 1.5之后提供了Callable和Future接口,通过它们就可以在任务执行完毕之后得到任务的执行结果)

同时还能看到源码中将执行结果Result和WorkRunnable(自身实现了CallBack接口)传给了FutureTask。下面看看FutureTask具体是什么。

public class FutureTask<V> implements RunnableFuture<V> {

private Callable<V> callable;

public FutureTask(Callable<V> callable) {
        //必须传入非空的实现CallBack接口的工作线程WorkRunnable
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
}

......
}

由上图知道FutureTask实现了RunnableFuture接口。官方注释解释FutureTask这个类提供异步任务启动和取消的方法。再看看RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

看到了RunnableFuture继承了Callable和Future接口,再看看Future接口

public interface Future<V> {

 boolean cancel(boolean mayInterruptIfRunning);

 boolean isCancelled();

 boolean isDone();

 V get() throws InterruptedException, ExecutionException;

 V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, 
 TimeoutException;
}
  • cancel():用来取消异步任务的执行。如果异步任务已经完成或者已经被取消,或者由于某些原因不能取消,则会返回false。如果任务还没有被执行,则会返回true并且异步任务不会被执行。如果任务已经开始执行了但是还没有执行完成,若mayInterruptIfRunning为true,则会立即中断执行任务的线程并返回true,若mayInterruptIfRunning为false,则会返回true且不会中断任务执行线程。
  • isCanceled():判断任务是否被取消,如果任务在结束(正常执行结束或者执行异常结束)前被取消则返回true,否则返回false。
  • isDone():判断任务是否已经完成,如果完成则返回true,否则返回false。需要注意的是:任务执行过程中发生异常、任务被取消也属于任务已完成,也会返回true。
  • get():获取任务执行结果,如果任务还没完成则会阻塞等待直到任务执行完成。如果任务被取消则会抛出CancellationException异常,如果任务执行过程发生异常则会抛出ExecutionException异常,如果阻塞等待过程中被中断则会抛出InterruptedException异常。
  • get(long timeout,Timeunit unit):带超时时间的get()版本,如果阻塞等待过程中超时则会抛出TimeoutException异常。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

现在总结一下:AsyncTask构造方法中,WorkRunnable方法中将任务参数Params传入doInBackground(),并将得到的结果和WorkRunnable对象传给了FutureTask,FutureTask实现了RunnableFuture接口,且RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask既能当做一个Runnable直接被Thread执行,也能作为Future用来得到Callable的计算结果。

现在理清了一条线:当WorkRunnable中定义的call方法被执行时,doInBackground就会开始执行,我们定义的后台任务也就真正开始了。

那么这个call方法什么时候会被调用呢?对了,就是AsyncTask.execute()。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}

看到第一个参数sDefaultExecutor,是啥?看源码

(对Executor不熟悉的可以看看:https://blog.csdn.net/qq_25806863/article/details/71126867)

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        //加上了锁,串行执行Runnable任务
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //执行完之后才去,判断是否有下个Runnable任务
                        scheduleNext();
                    }
                }
            });
            //为空的时候,也去判断任务队列中头部是否有任务Runnable
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //poll方法:拿到任务队列中的头部任务(为了方便,说成队列了)
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive); 
            }
        }
}

可以看到sDefaultExecutor,最终实例化了一个静态类SerialExecutor。确实是串行的执行任务,并且任务执行完,还会通过scheduleNext()函数看看队列顶部是否还有后台任务。

可以看到scheduleNext()中这么一句话THREAD_POOL_EXECUTOR.execute(mActive)。THREAD_POOL_EXECUTOR是啥?看源码。

   public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

THREAD_POOL_EXECUTOR看出是系统提早创建好的线程池。说了这么多其实一直在说executeOnExecutor()函数的第一个参数sDefaultExecutor,作用就是默认线程池串行的执行我们的任务。

跳出来,仔细看看executeOnExecutor()函数:

 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {

       //这2个异常解释了:为什么任务再执行或者finish阶段,不能再调用execute函数
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        //标记为运行状态
        mStatus = Status.RUNNING;

        //准备工作的方法
        onPreExecute();

        //任务执行参数-传递给WorkRunnable
        mWorker.mParams = params;

        //*******重点********
        //默认线程池或者指定的线程池,执行execute(),传入FuturTask对象
        exec.execute(mFuture);

        return this;
}

重点就是:当我们执行到exec.execute(mFuture)时,就是调用WorkRunnable(也就是AsyncTask构造方法中mWorker)中的Call()函数,这是任务执行参数又有了,所以mWorker的Call方法中的 doInBackground(mParams)函数就开始执行后台任务了。

上面这一套就是串行执行后台任务的流程,那么并行执行后台任务是什么流程?其实就是直接调用executeOnExecutor()这个函数,可以看到它的第一个参数就是传入我们的指定线程池,O了。

还有一个重要的方法:onPostExecute。还记得WorkRunnable中调用的这个postResult()函数吗。

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
}

这个函数,在后台任务执行完后,通过Handler发送了一个What==MESSAGE_POST_RESULT的消息,看看如何处理的。

 @Override
 public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                //调用了finish()函数,去处理result结果
                case MESSAGE_POST_RESULT:
                    // 这儿只有一个结果,因为串行执行任务的原因
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
}

再看看finish()

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } 
        //任务没被取消,会调用onPostExecute(result)处理
        else {
            onPostExecute(result);
        }
    //标记为完成状态
    mStatus = Status.FINISHED;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器