AsyncTask总结

AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。

一、简介

1.AsyncTask的泛型参数

public abstract class AsyncTask<Params, Progress, Result>

其中,三个泛型类型参数的含义如下:

  • Params:开始异步任务执行时传入的参数类型;
  • Progress:异步任务执行过程中,返回下载进度值的类型;
  • Result:异步任务执行完成后,返回的结果类型;

如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。

2.AsyncTask的核心方法

onPreExecute()

这个方法会在后台任务开始执行之间调用,在主线程执行。用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

doInBackground(Params...)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。

任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。

onProgressUpdate(Progress...)

当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,在主线程中进行,利用参数中的数值就可以对界面元素进行相应的更新。

onPostExecute(Result)

当doInBackground(Params...)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,在主线程中进行,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

onCancelled()

在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

 3.简单使用

class DownloadTask extends AsyncTask<Integer, Integer, String>{  

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    @Override
    protected String doInBackground(Integer... params) {
        for(int i=0;i<=100;i++){
            publishProgress(i);
            try {
                Thread.sleep(params[0]);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "执行完毕";
    }
    @Override
    protected void onProgressUpdate(Integer... progress) {
        //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数
        //但是这里取到的是一个数组,所以要用progesss[0]来取值
        //第n个参数就用progress[n]来取值
        tv.setText(progress[0]+"%");
        super.onProgressUpdate(progress);
    }
    @Override
    protected void onPostExecute(String result) {
        //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发
        //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"
        setTitle(result);
        super.onPostExecute(result);
    }
}

需要在主线程启动

new DownloadTask(1).execute();

二、源码分析

构造函数

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                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);
            }
        }
    };
}

初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象。 FutureTask实现了Runnable接口。

mWorker中的call()方法执行了耗时操作,即result = doInBackground(mParams);,然后把执行得到的结果通过postResult(result);,传递给内部的Handler跳转到主线程中。在这里这是实例化了两个变量,并没有开启执行任务。

execute()方法

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
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;
}

可以 看出,先执行了onPreExecute()方法,然后具体执行耗时任务是在exec.execute(mFuture),把构造函数中实例化的mFuture传递进去了。该方法执行过后会把任务的执行状态赋值为RUNNING,如果执行任务状态为RUNNING的话就会报错,也就是说一个实例不能执行两次。

SerialExecutor类

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);
        }
    }
}

SerialExecutor 内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的,即多个任务需要一个个添加到该队列中,然后执行完队列头部的再执行下一个,以此类推。因为SerialExecutor 是个静态内部类,是所有实例化的AsyncTask对象公有的,所以说多个地方提交异步任务执行其实不会并行执行而是串行执行。

在这个方法中,有两个主要步骤。

  1. 向队列中加入一个新的任务,即之前实例化后的mFuture对象。
  2. 调用 scheduleNext()方法,调用THREAD_POOL_EXECUTOR执行队列头部的任务。

由此可见SerialExecutor 类仅仅为了保持任务执行是串行的,实际执行交给了THREAD_POOL_EXECUTOR。

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

看名字也知道这是一个线程池,开启了一定数量的核心线程和工作线程,并允许核心线程超时回收

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发送

private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }

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

AsyncTask中实例化的Handler是静态内部类实现的,无论实例化多少AsyncTask对象用的都是一个Handler。并且为了可以把结果返回到主线程,要求AsyncTask必须在主线程实例化。

在handlerMessage的处理中将消息分为两类,一类是结果,走finish()方法,一类是进度,回调onProgressUpdate()

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

在finish()中又分成了两类,一类是任务取消,回调onCancelled(),一类是任务完成,回调onPostExecute()

三、注意事项

1.生命周期

AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);

2.内存泄漏

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

3. 结果丢失

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

4.AsyncTask并行执行

从Android3.0开始,我们可以通过直接调用AsyncTask方法的executeOnExecutor方法传入自己定义的线程池,你也可以传入AsyncTask内部已经定义的THREAD_POOL_EXECUTOR线程池。

四、总结

使用 AsyncTask是创建AsyncTask对象之后执行execute,创建AsyncTask对象的时候会同时创建一个WorkerRunnable 对象,并且以这个WorkerRunnable对象为参数会创建一个FutureTask对象,那么分析AsyncTask的原理就该从execute方 法开始了,执行execute方法首先会执行executeOnExecutor方法,并且传入一个SerialExecutor类型的对 象,SerialExecutor是一个串行线程池,一个线程里面的所有AsyncTask全部都在这个串行的线程池中排队执行,在 executeOnExecutor里面首先会执行onPreExecute方法,该方法是在我们创建AsyncTask对象的时候自己实现的,运行在主 线程中,我们可以在这个方法里面进行任务开始的提示性操作,接着线程池开始执行,也就是从这一步开始切换到了子线程中,传入的对象就是我们创建 AsyncTask对象的时候生成的FutureTask对象,在SerialExecutor线程池的execute方法中首先会把当前 FutureTask对象插入到任务队列中,如果当前任务队列中没有正在活动的AsyncTask任务的话,则会执行scheduleNext方法从队列 中取得一个AsyncTask任务,同时当一个AsyncTask任务执行结束之后会在finally中调用scheduleNext方法执行任务队列中 的下一个AsyncTask任务,从这里也看出来默认情况下AsyncTask是串行执行的,真正的执行操作就该在scheduleNext方法里面 了,这个方法里面真正执行任务的线程池是THREAD_POOL_EXECUTOR,SerialExecutor线程池是是用来任务排队的,保证默认情况下的串行执行,而THREAD_POOL_EXECUTOR才是真正的任务执行者,此外在 AsyncTask里面还有一个InternalHandler对象,其实他就是一个Handler对象而已,他存在的作用就是为了从子线程切换到主线程 中,为了便于在子线程执行的过程中进行一些与界面元素的交互过程,比如下载进度条的更新等等,那么也就必须要求该InternalHandler对象在主线程中创建了,查看源码发现InternalHandler对象是static的,也就是在AsyncTask对象创建的时候他就会创建,因此只要保证AsyncTask对象在主线程中创建就可以了,因此使用AsyncTask的时候一定要注意在主线程中创建他的对象,THREAD_POOL_EXECUTOR会执行他的execute方法,该方法实际上执行的是FutureTask的run方法,而 FutureTask的run方法实际上执行的是创建FutureTask对象的时候传入的参数WorkerRunnable对象的call方法,查看 call方法可以看到执行了doInBackground方法,该方法也是需要我们在创建AsyncTask对象的时候自己实现的,我们可以在这个方法里 面执行一些比较耗时的操作,它运行在子线程中,在该方法中我们可以通过publishProgress来发送一些耗时任务已经处理的进度信息,该方法运行在子线程中,该方法中会通过InternalHandler将进度消息发送出去,接着在InternalHandler里面的handleMessage 里面会发现是通过onProgressUpdate进行消息处理的,该方法运行在主线程中,可以进行更新进度条的一些操作,在 doInBackground方法执行结束后会将返回结果作为参数传递给postResult方法,该方法同样会通过InternalHandler发送 消息,最后在InternalHandler里面的handleMessage里面处理该消息,调用的是finish方法,也就是将线程切换到了主线程中 了,在finish方法中会根据主线程有没有被暂停来执行onCancelled或者onPostExecute方法,这两个方法是运行在主线程的,到这 里AsyncTask的执行结束了;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章