Android之AsyncTask的学习笔记

来源:互联网 发布:唐山学院网络教育 编辑:程序博客网 时间:2024/04/29 02:45

    AsyncTask是轻量级的异步任务类,它可以在线程池中执行任务,然后把执行任务的进度和最终结果返回给主线程进行UI更新。从组成结构来讲,AsyncTask用到了Thread和Handler,通过AsyncTask可以更好的执行异步任务以及在主线程中访问UI。但是AsyncTask并不适合执行特别耗时的操作,对于特别耗时的操作,建议使用线程池。
    AsyncTask是一个抽象泛型类,其提供了params,progress,result三种泛型参数,其中params是指执行任务的参数的类型,progress是指异步任务返回进度的类型,result是指异步任务返回结果的类型,如果确实不需要这三个泛型参数,可以使用void来代替。

public abstract void AsyncTask<params,progress,result>

1.onPreExecute,这个方法是在主线程中执行,在异步任务开始前被调用,一般用于做一些准备的工作。
2.doInBackground 这个方法在线程池中执行,用于执行异步任务,params参数表示执行任务需要的参数,可以通过publishProgress方法返回当前执行任务的进度,publishProress会调用 onProgressUpdate方法。而且这个方法返回的结果类型会传递给onPostExecute。
3.onProgressUpdate,这个方法在主线程中执行,用于返回当前异步任务的进度。
4.onPostExecute 这个方法在主线程中执行,当异步任务结束后调用该方法,用于返回任务执行的结果。
    在上面的方法中,首先执行onPreExecute,接下来是doInBackground,最后是onPostExecute,AsyncTask还提供了onCancelled方法,当异步任务被取消时,onCancelled方法会被调用,但是onPostExecute不会被调用。下面提供一个典型的用法,如下所示。

public class DownLoadFilesTask extends AsyncTask<URL,Integer,Long>{    @Override    protected void onPreExecute() {        super.onPreExecute();    }    @Override    protected Long doInBackground(URL... urls) {        int count = urls.length;        long totalSize = 0;        for (int i = 0; i < count; i++) {            totalSize += Downloader.downloadFile(urls[i]);            publishProgress((int) i / (float)count) * 100));            if (isCancelled()){                break;            }        }        return totalSize;    }    @Override    protected void onProgressUpdate(Integer... values) {        super.onProgressUpdate(values);        setProgressPercent(values[0]);    }    @Override    protected void onPostExecute(Long result) {        super.onPostExecute(result);        showDialog("Downloaded"+ result +"bytes");    }}

    在上面的代码中,实现了一个具体的AsyncTask类,这个类主要是模拟异步任务去下载文件的一个过程,它的输入参数类型是URL,后台任务的进度参数为Integer,而后台返回的结果为Long类型。可以看到doInbackground和onProgressUpdate方法中它们的参数类型均包含…的字样,在Java中…表示数量不定,它是一种数组型参数,…的概念和C语言的…是一致的。当要执行上述下载任务时,可以通过以下方式去调用。

new DownLoadFilesTask().execute(url1,url2,url3);

    在DownloadFilesTask中,doInBackground用来执行具体的下载任务并通过publishProgress方法来进行更新进度,同时还要判断是否被取消了。当下载任务结束后,doInBackground会返回结果,即下载的总字节数。需要注意的是,doInBackground是在子线程中进行的。onProgressUpdate用于更新任务下载的进度,它运行在主线程中,当publishProgress被调用时,此方法也会被调用。当下载任务完成后,onPostExecute方法也会被调用,它也是运行在主线程中,这个时候我们就可以在界面上进行一个提示的操作,告知用户已经下载成功。
    AsyncTask在具体的使用过程也是有一些限制的,主要有如下几点:
1.AsyncTask的类比较在主线程中加载,这就意味着第一次开启AsyncTask的时候必须在主线程。不过在Android4.1及以上版本中已经被系统自动完成。在Android5.0源码中,可以查看ActivityThread中的main方法,它会调用AsyncTask的init方法,这就是满足了AsyncTask的类比较在主线程中加载的条件。
2.AsyncTask的对象必须在主线程中创建。
3.execute方法必须在UI线程中调用。
4.不要在程序中直接调用                onPreExecute,doInBackground,onProgressUpdate,onPostExecute方法。
5.一个AsyncTask只能被执行一次,即execute只能被调用一次,否则会报运行时异常。
6.在Android1.6之前,AsyncTask是串行执行任务的,在Android1.6的时候,AsyncTask开始采用线程池来处理并行任务,但是在Android3.0开始,AsyncTask为了避免因为并行任务造成的错误,AsyncTask又采用了一个线程执行串行任务,尽管如此,在Android3.0及以上版本中,我们仍然可以通过调用executeOnExecutor方法来并行的处理任务。

AsyncTask的工作原理

    为了分析AsyncTask的工作原理,我们从它的execute方法开始分析,execute方法又会调用executeOnExecutor方法,如下图所示:

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

    在上面的代码中,sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行,这个排队执行的过程后面会进行分析。在executeOnExecutor方法中,AsyncTask的onPreExecute方法最先执行,然后线程池开始执行。下面分析线程池中的执行过程,如下所示:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;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的排队执行过程。首先系统会把AsyncTask的Params参数封装为FutureTask对象,FutureTask是一个并发类,在这里充电了Runnable的作用。接着这个FutureTask会交给SerialExecutor的execute方法去处理,SerialExecutor的execute方法首先会把FutureTask对象插入到任务队列mTasks中,如果这个时候没有活动的AsyncTask任务,那么就会调用SerialExecutor的scheduleNext方法执行下一个AsyncTask任务。同时当一个AsyncTask任务执行完成后,AsyncTask会继续执行其他任务直到所有的任务都被执行为止,从这一点开始,在默认情况下,AsyncTask是串行执行任务的。
    AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正执行任务,InternalHandler用于将执行环境从线程池切换到主线程。在AsyncTask的构造方法中有如下这么一段代码,由于FutureTask的run方法会调用mWorker的call方法,因此mWorker的call方法最终会在线程池中执行。

mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Result result = null;                try {                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                    //noinspection unchecked                    result = doInBackground(mParams);                    Binder.flushPendingCommands();                } catch (Throwable tr) {                    mCancelled.set(true);                    throw tr;                } finally {                    postResult(result);                }                return result;            }        };

    在mWorker的call方法中,首先将mTaskInvoked设为true,表示当前任务已经被调用了,然后执行AsyncTask的doInBackground方法,接着讲其返回值传递给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方法会通过sHandler发送一个MESSAGE_POST_RESULT的消息,这个sHandler的定义如下所示:

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:                    // There is only one result                    result.mTask.finish(result.mData[0]);                    break;                case MESSAGE_POST_PROGRESS:                    result.mTask.onProgressUpdate(result.mData);                    break;            }        }    }

    可以发现,sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就要求sHandler这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相要求AsyncTask的类必须在主线程中加载,否则在同一个进程中AsyncTask都将无法正常工作。sHandler收到MESSAGE_POST_RESULT这个消息后会调用AsyncTask的finish方法,如下所示:

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

    AsyncTask的finish方法的逻辑比较简单,如果AsyncTask被取消执行了,那么就调用onCancelled方法,否则就会调用onPostExecute方法,可以看到doInBackground的返回结果传递给onPostExecute方法,到这里SyncTask的整个工作过程就分析完毕了。

ps:本文摘自《Android开发艺术探索》

0 0
原创粉丝点击