Android 多线程:AsyncTask理解和使用总结

2017-10-19  本文已影响0人  Marker_Sky
AsyncTask目录.png

一、AsyncTask介绍

1.1 作用:

轻量级的异步类,同时方便在子线程更新UI。

1.2 原理:

封装了Handler和两个线程池。AsyncTask的源码链接

  1. 线程池THREAD_POOL_EXECUTOR:通过ThreadFactory和一些参数实例化线程池THREAD_POOL_EXECUTOR用于执行任务
  2. 线程池SERIAL_EXECUTOR:内部类SerialExecutor实现Executor也是一个线程池,实例化对象SERIAL_EXECUTOR执行SerialExecutor定义的execute方法来串行(一个一个按顺序)排队任务并使用THREAD_POOL_EXECUTOR来执行任务
  3. AsyncTask创建时会实例化一个WorkerRunnable对象mWorker和一个FutureTask对象mFuture。FutureTask是一个并发类,充当Runnable的作用。AsyncTask执行任务时把mFuture作为排队线程池SerialExecutor的execute(final Runnable r)的参数传递,接着会串行排队并使用工作线程来处理实际任务。
  4. mFuture对象创建时把mWorker对象作为参数传递,在mFuture在执行任务run()时会调用mWorker的call()方法,因此call()方法是在线程池中进行的。
  5. 主线程的Handler:然后call()方法里面接着调用result = doInBackground(mParams);并返回Result对象,这个过程中任务被取消会catch跳出设置AsyncTask结束。call()方法最后postResult(result);获取主线程的Handler发送消息并根据消息类型判定更新UI或者结束AsyncTask。
    具体参考:源码解析Android中AsyncTask的工作原理

1.3 参数以及核心函数

AsyncTask是一个泛型类,在android.os包下

AsyncTask类.png
泛型内三个参数定义的是我们需要的数据的类型,如果不需要传递参数,可以用Void来代替,以下是具体含义:
  1. protected void onPreExecute():在主线程执行,异步任务执行前调用做准备工作;
  2. protected abstract Result doInBackground(Params... var1):在线程池中执行,用于执行异步任务。Params表示异步任务输入参数,可以在此方法中通过publishProgress方法来更新任务进度,publishProgress方法又调用onProgressUpdate方法实现主线程进度更新。这个方法返回ResultonPostExecute方法。
  3. protected void onProgressUpdate(Progress... values):在主线程执行,后台任务执行进度发生变化会调用此方法。
  4. protected void onPostExecute(Result result):在主线程执行,异步任务执行后调用,result参数是由doInBackground返回的。

这四个方法执行的顺序为:onPreExecute(准备)-->doInBackground(后台异步)-->onPostExecute(结果处理)
还有一个有用的方法:protected void onCancelled():当异步任务取消时调用,而这时候onPostExecute不会被调用。

二、AsyncTask使用

2.1 AsyncTask使用示例:

(1) 创建Activity,布局两个按钮用来开始和取消AsyncTask,一个TextView来显示状态。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_download"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始下载"/>

    <Button
        android:id="@+id/btn_cancel"
        android:layout_below="@id/btn_download"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="取消下载"/>

    <TextView
        android:id="@+id/tv_statue"
        android:layout_below="@id/btn_cancel"
        android:textSize="15sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

(2) 创建内部类自定义MyAsyncTask继承AsyncTask,重写四个主要方法:

  //第一个参数:Params参数类型:String类型 
  //第二个参数:Progress执行进度类型:Object类型 
  //第三个参数:Result参数类型:Long类型
  private class MyAsyncTask extends AsyncTask<String,Object,Long>{
  ...
  @Override
  protected void onPreExecute() {...}

  @Override
  protected Long doInBackground(String... strings) {...}

  @Override  
  protected void onProgressUpdate(Object... values) {...}

   @Override
   protected void onPostExecute(Long aLong) {...}

  @Override //任务取消调用
  protected void onCancelled() {...}

(3)开始按钮调用创建MyAsyncTask并执行downloadTask.execute(urls)传入参数。

mDownButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String[] urls = {
                "http://www.jianshu.com/p/d3984e7e204f",
                "http://www.jianshu.com/p/8e3cfb87f35e",
                "http://www.jianshu.com/p/4fbc2ea2e02b"
        };
        downloadTask = new MyAsyncTask();
        // 这里的url也可以分成一个一个参数传递
        downloadTask.execute(urls);
    }
});

(4) 取消按钮调用downloadTask.cancel(true)停止MyAsyncTask。

//取消按钮点击事件
downloadTask.cancel(true);

(5) doInBackground(String... strings)方法循环执行下载任务并返回result,是自己定义的二维数组,第一个是字节数,第二个是文章标题。

protected Long doInBackground(String... strings) {
    long totalByte = 0;
    for (String url:strings){
        // downloadFile方法只是一个简单的下载字节流
        // 返回的Object[] result是二维数组,一个是下载的字节数,一个是标题
        Object[] result = downloadFile(url);
        int byteCount = (int) result[0];
        totalByte += byteCount;
        //传递进度
        publishProgress(result);
        // 异步任务调用取消则跳出for循环
        if(isCancelled()){
            break;
        }
    }
    return totalByte;
}

(6) 然后使用publishProgress(result);把数据传递给onProgressUpdate(Object... values)方法来更新UI,这个方法已经切换到了主线程,所以可以更新UI。AsyncTask完成后onPostExecute(Long aLong)方法自动调用再次来更新状态。

  @Override
  protected void onPostExecute(Long aLong) {...}

看一下效果,源码我会在后面放出,取消下载请自行测试。


AsyncTaskDemo.gif

2.2 AsyncTask使用注意:

  1. AsyncTask的类必须在主线程加载。
  2. AsyncTask的对象必须在主线程创建。
  3. execute方法必须在UI线程调用。
  4. 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则报运行时异常。
  5. 在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始使用线程池处理并行任务,但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程串行执行任务。尽管如此,在Android3.0以及后续的版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行地执行任务。

三、AsyncTask优点和缺点

3.1 优点:

  1. 简单快捷,使用方便。
  2. UI更新及时,过程可控。

3.2 缺点:

多个异步操作需要更新UI时,就变得麻烦起来。

3.3 其它问题:

参考文章:AsyncTask的缺陷和问题

目录文章:
Android多线程:理解和简单使用总结

上一篇下一篇

猜你喜欢

热点阅读