AsyncTask源码阅读

AsyncTask使用

Android中的耗时操作需要放到子线程异步处理,除了使用Thread,Android提供了一个封装好的组件AsyncTask。
使用方法代码如下:
public class AsyncTaskActivity extends AppCompatActivity {

private TextView mProgress;
private ImageView mImage;
private MyDownloadAsyncTask myDownloadAsyncTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_async_task);
    initView();
}

public void download(View view) {
    myDownloadAsyncTask = new MyDownloadAsyncTask();
    myDownloadAsyncTask.execute("https://www.baidu.com/img/baidu_jgylogo3.gif");
}

public void cancel(View view) {
    myDownloadAsyncTask.cancel(true);
}

private void initView() {
    mProgress = (TextView) findViewById(R.id.progress);
    mImage = (ImageView) findViewById(R.id.image);
}

public class MyDownloadAsyncTask extends AsyncTask<String, Integer, Bitmap> {
    @Override
    protected void onPreExecute() {
        //doInBackground执行之前
        mProgress.setText("下载任务准备开始");
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        //doInBackground执行之后
        if (bitmap != null) {
            mImage.setImageBitmap(bitmap);
        }

    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //doInBackground执行中
        mProgress.setText("任务进行中:" + values[0] + "%");
    }
    @Override
    protected Bitmap doInBackground(String... strings) {
        Log.e("MyDownloadAsyncTask", "ThreadName:" + Thread.currentThread().getName());
        try {
            URL url = new URL(strings[0]);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            // 使用 URL 连接进行输出
            httpURLConnection.setDoOutput(true);
            // 使用 URL 连接进行输入
            httpURLConnection.setDoInput(true);
            // 忽略缓存
            httpURLConnection.setUseCaches(false);
            // 设置URL请求方法
            httpURLConnection.setRequestMethod("GET");

            InputStream inputStream = httpURLConnection.getInputStream();
            int contentLength = httpURLConnection.getContentLength();

            ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
            byte[] buff = new byte[100];
            int length = 0;
            int sum = 0;
            while ((length = inputStream.read(buff)) > 0) {
                swapStream.write(buff, 0, length);
                sum += length;
                double percent = sum * 1.0f / contentLength * 100;
                publishProgress((int) percent);
            }
            byte[] bytes = swapStream.toByteArray();
            Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            return bitmap;

        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }
}

}
三个泛型 Params, Progress, Result分别对应传入的参数类型,中间进度的类型,返回结果的类型。AsyncTask主要有doInBackground、onPreExecute、onPostExecute、onProgressUpdate四个个方法。
doInBackground:必须要重写,异步执行后台线程,要完成的耗时任务在这里处理,运行在子线程。
onPreExecute:执行doInBackground前被调用,进行耗时操作前的初始化或者准备工作,运行在主线程。
onProgressUpdate:doInBackground执行完成后调用,返回处理结果,更新UI等,运行在主线程。
onProgressUpdate:在doInBackground方法中调用publishProgress方法后调用更新耗时任务进度,运行在主线程。
上面代码中实现了自己的MyDownloadAsyncTask完成一个图片下载的简单Demo。三个泛型分别为String、Integer、Bitmap类型,对应传入参数图片地址、下载进度、和下载完成的图片。doInBackground方法中发送网络请求下载图片,调用publishProgress在onProgressUpdate中实现进度更新。然后在按钮点击事件中调用execute方法开始执行。除此之外,调用cancel可以取消任务。

源码阅读

AsyncTask一共不多七百多行代码,所以可以先看一下其中包含的方法和成员变量。

先来看下成员变量的具体有哪些:

/**
 * 日志Tag
 */
private static final String LOG_TAG = "AsyncTask";

/**
 * CPU核心数
 */
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
 * 线程池核心线程数
 */
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
/**
 * 线程池最大线程数
 */
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
/**
 *  线程闲置超时时间
 */
private static final int KEEP_ALIVE_SECONDS = 30;

/**
 * 线程工厂
 */
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    /**
     *  保证原子性 支持原子指令Integer型
     */
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
/**
 * LinkedBlockingQueue链表结构有序阻塞队列用来存取任务  容量为128
 */
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * 执行任务的线程池
 */
public static final Executor THREAD_POOL_EXECUTOR;
/**
 * 任务排队的线程池
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

/**
 * 结果消息
 */
private static final int MESSAGE_POST_RESULT = 0x1;
/**
 * 进度消息
 */
private static final int MESSAGE_POST_PROGRESS = 0x2;

/**
 *  任务排队的线程池 SerialExecutor
 */
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
 * UI线程主线程的Handler
 */
private static InternalHandler sHandler;

/**
 *  一个封装了带Params的Callable抽象类,用来执行任务
 */
private final WorkerRunnable<Params, Result> mWorker;
/**
 * 处理结果任务
 */
private final FutureTask<Result> mFuture;

/**
 * AsyncTask的状态
 */
private volatile Status mStatus = Status.PENDING;
/**
 * 当前任务是否被取消
 */
private final AtomicBoolean mCancelled = new AtomicBoolean();
/**
 * 当前任务是否被调用
 */
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

接下来看AsyncTask的几个内部类:

/**
 * 存放任务结果
 * @param <Data>
 */
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

/**
 * 主线程Handler
 */
private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

/**
 * 由ArrayDeque队列构成的串行线程池
 */
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

/**
 * 封装了带Params的Callable抽象类
 * @param <Params>
 * @param <Result>
 */
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

/**
 * 状态枚举
 *
 */
public enum Status {
    /**
     * 未执行状态
     */
    PENDING,
    /**
     * 运行状态
     */
    RUNNING,
    /**
     * 运行完成状态
     */
    FINISHED,
}

搞清楚了有些啥,就来可以研究AsyncTask到底是怎么运行的了。从使用方法来看,是先自定义了一个AsyncTask并且new了一个实例,那么首先要走的就是构造方法。

 public AsyncTask(@Nullable Looper callbackLooper) {
    /**
     * 初始化mHandler
     */
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
    /**
     * 初始化WorkerRunnable
     */
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                //线程优先级设置后台线程
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //执行doInBackground返回result
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                //异常则取消
                mCancelled.set(true);
                throw tr;
            } finally {
                //调用postResult方法
                postResult(result);
            }
            return result;
        }
    };
    /**
     * 初始化FutureTask
     */
    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);
            }
        }
    };
}

构造方法中初始化了一个Handler,因为传入是空所以拿到的Handler是主线程的Handler,通过此Handler接收消息处理与主线程的交互。除此之外还初始化一个WorkerRunnable,这个抽象类之前看过知识继承了Callable接口,并且保存了一个我们传入的参数Params。在call方法中调用了doInBackground方法并返回result。如果catch到异常就会取消任务,最终都会执行postResult方法将运行结果发送出去。

postResult方法的代码:

 /**
 * 发送提交结果的消息
 * @param result
 * @return
 */
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

发送message给handler处理,msg.what为MESSAGE_POST_RESULT,msg.obj传一个AsyncTaskResult 。

在回到构造函数,看到接着new了一个FutureTask对象,并且将worker传入。

可以看到FutureTask继承RunnableFuture接口,而RunnableFuture又继承了Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。在done方法里get获取到mWorker的call方法的返回值后会调用postResultIfNotInvoked方法。

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

在postResultIfNotInvoked方法里根据mTaskInvoked状态执行postResult方法。至此构造函数结束。

除了构造方法,静态代码块也会在类加载时运行,接着看静态代码块里的内容:

/**
 * 静态代码块中初始化线程池
 */
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;
}

静态代码块中只是初始化了执行任务的线程池。

接下来我们在是使用时调用了AsyncTask的execute方法,来看execute方法。

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

execute方法调用了executeOnExecutor方法传入了参数和sDefaultExecutor线程池。sDefaultExecutor是一个串行任务队列线程池。

/**
 * 任务排队的线程池
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

/**
 *  任务排队的线程池 SerialExecutor
 */
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

/**
 * 由ArrayDeque队列构成的串行线程池
 */
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

sDefaultExecutor由一个数组结构的双向队列构成,从队列中取出任务后放入THREAD_POOL_EXECUTOR执行。

继续看executeOnExecutor方法:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                   Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            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();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

首先是根据mStatus状态,Running或者Finish则会抛出异常。所以一个AsyncTask只能调用execute方法一次。若状态正确则把Status修改为Running 。然后调用onPreExecute()方法,并且将传入的params传给mWorker中,然后调用线程池的execute方法传入mFuture。任务执完成后发送消息。Handler接收到根据message.what的类型分别调用finish方法和onProgressUpdate方法。调用finish方法再会根据是否取消状态来调用onPostExecute和onCancelled方法,至此工作流程结束。

  /**
   * 主线程Handler
   */
private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

 private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

AsyncTask的工作流程图:

AsyncTask的结构图:

AsyncTask的几个注意点

1、AsyncTask 对象必须在主线程创建。

sHandler是静态的对象,为了能够将环境切换到主线程,就必须要求这个对象必须在主线程创建,由于静态成员变量会在类加载时候进行初始化,也就变相要求AsyncTask的类必须在主线程中加在。

2、Android3.0之前AsyncTask是并行执行任务,3.0之后为串行执行。

从3.0之后的源码,可知SerialExecutor是一个串行的任务线程池,所以3.0之后的AsyncTask是串行执行任务。
3.0之前,源码中并没有SerialExecutor这个线程池,只有一个任务执行线程池,所以是并发的。3.0之后想要并行执行任务,只需要调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params)设置即可。

3、内存泄漏

在Activity中使用AsyncTask,AsyncTask持有Activity的引用,如果在网络请求没有结束,但是Activity已经被关闭的情况下,Actuvity的引用的不到释放,就会造成内存泄漏。

解决方法:
1、将AsyncTask设置为static,设置成静态对象后,在Activity类初始化的时候会初始化,会占用一定内存,但是会和Activity同生命周期,Activity销毁时AsyncTask也会被销毁。
2、使用弱引用WeakReference,这样gc运行时就会回收,避免内存泄漏。
AsyncTask不是有cancel方法吗?在activity生命周期结束前调用cancel方法不就行了吗?为什么不行呢?
原因:
  public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

futreTask的cancel方法

  public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          U.compareAndSwapInt(this, STATE, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                U.putOrderedInt(this, STATE, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}
查看cancel的源码可知AsyncTask的cancel调用了FutrueTask的cancel,归根结底是调用了线程的interrupt()方法,而interrupt()方法不会真正的中断一个正在运行的线程,而是发出中断请求。然后由线程在一个合适的时刻中断自己。
参考资料:Android进阶之光、Android开发艺术探索
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值