AsyncTask的使用和工作原理

作者: 可乐游侠 | 来源:发表于2018-08-26 20:49 被阅读43次

    简介

    AsyncTask是一个轻量级的异步任务类,封装了Handler和Thread,可以方便的在线程池里执行后台任务,并把进度和结果发送到主线程并在主线程中更新UI。AsyncTask应该用来执行比较短的后台任务,一般是以秒为单位的。如果需要保持线程长期运行,建议用线程池进行。

    一个AsyncTask是一个抽象的泛型类,通过继承的方式来使用。AsyncTask需要指定三个三个泛型参数的类型,重写四个回调方法。

    三个泛型

    三个泛型参数分别是Params, Progress and Result

    • Params:初始化AsyncTask时传入的参数类型,用来定义传递给后台任务的参数类型。
    • Progress:后台任务执行进度的类型。
    • Result:后台任务执行完成返回的结果类型。

    当不需要相应的参数的时候,也可定义成Void类型。

    四个方法

    • onPreExecute(),后台任务执行前在UI线程中调用,一般用来做一些初始化工作,比如显示progress bar等。
    • doInBackground(Params...),在onPreExecute()方法执行之后,在后台线程中调用,用来处理耗时的后台任务,执行完成之后将结果return。在这个方法中可以调用publishProgress(Progress...)将执行进度发送到主线程的回调方法onProgressUpdate(Progress...)。
    • onProgressUpdate(Progress...),在调用publishProgress(Progress...)方法之后会在主线程中回调这个方法。主要用来更新后台任务的执行进度。
    • onPostExecute(Result),在后台任务执行完成之后在UI线程中调用。后台任务执行结果会作为一个参数传递到这个方法中,一般用来更新UI。

    取消任务

    AsyncTask可以调用cancel(boolean)方法取消执行,该方法调用之后,调用isCancelled()方法会返回true,后台任务执行完成后,不再调用onPostExecute(Object),而是调用onCancelled(Object)。为了尽快的取消任务,应该在doInBackground(Object[])方法中尽可能的调用isCancelled()判断任务是否被取消。

    使用方法

    AsyncTask必须通过继承的方式使用。子类至少复写onInBackground(Params...)方法,一般还会复写onPostExecute(Result)方法。如下使用一个模拟下载文件的例子:

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
         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));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
             return totalSize;
         }
    
         protected void onProgressUpdate(Integer... progress) {
             setProgressPercent(progress[0]);
         }
    
         protected void onPostExecute(Long result) {
             showDialog("Downloaded " + result + " bytes");
         }
     }
    

    写好子类之后,执行就比较简单。

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

    注意事项

    AsyncTask使用有一些限制,需要注意:

    • AsyncTask类必须在UI线程中加载。在Android4.1之后由系统自动完成。
    • AsyncTask实例必须在UI线程中创建。
    • execute(Params...)方法必须在UI线程中调用。
    • 不要手动调用提供的四个回调方法。
    • 每个AsyncTask实例只能执行一次。

    版本演化

    AsyncTask类经过几次的版本演化,一开始的AsyncTask是顺序执行的,从Android1.6开始,改成了线程池,允许多个AsyncTask并行执行。直到Android3.0开始,为了避免并发引起的错误,AsyncTask又改成默认为顺序执行,但可以通过executeOnExecutor()方法并发执行。

    AsyncTask的工作原理

    为了分析AsyncTask的工作原理,从execute(Params... params)方法入手。

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

    execute(Params... params)方法又调用了executeOnExecutor(Executor exec, Prams... 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;
    }
    

    从上面的源码可以看到,在AsyncTask执行的时候,就调用了onPreExecute()方法,然后将params参数封装成FutureTask对象,调用了线程池sDefaultExecutor的execute(mFuture)方法。线程池sDefaultExecutor实际就是一个串行的线程池,我们来看一下sDefaultExecutor线程池的实现。

    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;
    
            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中,FutureTask作为Runnable对象传进来之后,会把FutureTask插入任务队列mTasks中,如果当前没有正在活动的AsyncTask,就会调用sheduleNext()方法,如果有正在活动的AsyncTask,则在执行结束之后调用sheduleNext()方法,直到所有的任务执行完成。可以看得出来SerialExecutor是串行执行的。而真正执行的线程池为THREAD_POOL_EXECUTOR,SerialExecutor是对THREAD_POOL_EXECUTOR的封装。

    在THREAD_POOL_EXECUTOR的execute()方法中最后调用了run方法。

    public void run() {
        ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
        ...
    }
    

    而在FutureTask执行run方法时,会执行Callable的call()方法,从AsyncTask的构造方法中可以发现,对应的是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;
        }
    };
    
    mFuture = new FutureTask<Result>(mWorker) {
    ...
    

    在call()方法中,执行了doInBackground(mParams)方法,然后将返回值传递给postResult(result)中。

    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)方法中,将result包装成Message,发送给Handler,跟踪getHandler()找到对应的Handler为InternalHandler。

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

    InternalHandler收到MESSAGE_POST_RESULT消息之后,调用AsyncTask的finish()方法。

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

    如果AsyncTask取消执行了,就调用onCancelled()方法,否则调用onPostExecute()方法。

    相关文章

      网友评论

        本文标题:AsyncTask的使用和工作原理

        本文链接:https://www.haomeiwen.com/subject/mlcaiftx.html