美文网首页
深入理解AsyncTask

深入理解AsyncTask

作者: 匿名用户_bcc3 | 来源:发表于2018-11-25 18:40 被阅读0次

主线程和子线程

主线程是指进程所拥有的线程,在Java中默认一个进程只有一个线程,这个线程就是主线程。主线程主要用于处理界面交互相关的逻辑,因为用户随时都可能会和界面发生交互,因此主线程在任何时候都必须要有较快的响应速度,否则就会给人一种卡顿的感觉。这就要求主线程不能处理太耗时的任务,比如网络请求、IO操作等。这样,只能把网络请求、IO这些耗时操作放到子线程中去处理。所以,正常一个带有网络接口请求的应用,那么至少会需要两个线程,一个主线程负责处理交互,一个子线程负责处理接口请求,那么子线程请求接口之后拿到的数据,怎么传递给主线程去更新UI呢?我们可以用Handler来处理子线程与主线程的交互。而AsyncTask就是封装了Handler+线程池来处理子线程与主线程交互的一个API,方便了开发者,不用每次都自己创建线程和写Handler了。

AsyncTask基本使用

创建一个下载文件的异步任务

private class DownloadFileTask 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");
    }
}

执行该异步任务

DownloadFileTask downloadTask = new DownloadFileTask().execute(url1,url2,url3);

AsyncTask是一个抽象的泛型类,主要接受三个参数,分别是Params、Progress、Result,其中Params主要是AsyncTask执行时传递进来的参数类型,Progress是在异步任务执行的过程中的参数类型,Result是异步任务结束之后的类型。另外,AsyncTask提供了4个核心方法,分别如下:

  • onPreExecute(),在主线程中执行,在异步任务开始执行之前调用,一般用于任务执行前的准备工作
  • doInBackground(Params... param),在线程池中执行,开始执行异步执行任务,param是通过AsyncTask的execute方法传递进来的,在这个方法中可以通过publishProgress来更新任务的进度,这个方法会调用到onProgressUpdate
  • onProgressUpdate(Progress... progress),在主线程中执行,用于更新进度。
  • onPostExecute(Result result),在主线程中执行,异步任务执行结束之后执行,其中result参数是doInBackground的返回值。

取消AsyncTask异步任务

downloadTask.cancel(true);

注意:这里的cancel方法的参数既可以传true也可以传false,其中true表示可以中断正在执行的任务。另外,虽然调用了这个cancel方法,但是doInbackground的异步任务,并不会立即结束,只是不会调用onProgressUpdate和onPostExecute。举个例子,doInBackground中有个循环,当执行cancel并不会中断这个循环,如果需要中断这个循环,那么可以在doInBackground中添加一个判断逻辑,如果isCancelled()则return。

AsyncTask在具体的使用过程中也有一些条件限制,主要有如下几点:

  • AsyncTask这个类必须在主线程中加载,不过这个在Android4.1及以上版本中已经被系统自动完成。
  • 必须在主线程创建该实例对象
  • 必须在主线程调用execute(Params...)
  • 不要手动调用上面的四个核心方法
  • 一个AsyncTask任务只能execute一次,否则会报运行时异常

AsyncTask的发展史
在一开始出现AsyncTask时,只有一个子线程来执行异步任务。从Android1.6开始,从单线程改成了线程池了支持并发任务了。然后从3.0开始为了解决并发导致的错误又改成了串行执行了。如果你还是想并发执行任务,可以调用AsyncTask的executeOnExecutor方法。

AsyncTask的工作原理

为了分析AsyncTask的工作原理,我们先分析它的execute方法,execute方法又调用了executeOnExecutor方法,它们的实现如下:

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

   @MainThread
    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方法,然后把Params封装到FutureTask对象中了,最后开始执行sDefaultExecutor这个默认线程池。下面分析这个线程池的执行过程,代码如下:

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首先会将FutureTask插入到队列中,如果发现没有正在执行的Task,那么就从队列中取出一个Task放到THREAD_POOL_EXECUTOR线程池中执行,当执行完成之后再会调用scheduleNext,直到mTasks中的Task都执行完成为止。FutureTask已经放到THREAD_POOL_EXECUTOR线程池中了,那么就会开始执行这个FutureTask了,FutureTask的实现如下:

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

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

FutureTask的执行从call方法开始,在call中调用了第二个核心方法,就是doInBackground,然后拿到doInBackground的返回值通过postResult(result)传递给AsyncTask的另外一个重要角色InternalHandler,这样就通过Handler机制实现了从子线程传递数据到主线程了,然后在Handler中执行了第四个重要方法onPostExecute来更新UI。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是个静态的内部类,为了能够将执行环境切换到主线程,这就要求创建的Handler对象必须在主线程创建。由于静态成员必须在类加载时初始化,所以才会要求AsyncTask的类必须在主线程中加载。

参考:
https://developer.android.com/reference/android/os/AsyncTask
《Android开发艺术探索-任玉刚》

相关文章

  • 深入理解 AsyncTask

    AsyncTask 是一个简单实用的多线程异步任务工具类。 Android 开发中经常遇到需要将耗时的操作放到子线...

  • 深入理解AsyncTask

    主线程和子线程 主线程是指进程所拥有的线程,在Java中默认一个进程只有一个线程,这个线程就是主线程。主线程主要用...

  • Android 面试题之线程间通信机制

    为什么一定要在主线程中创建AsyncTask的对象呢? 深入理解AsyncTask 为什么一定要在主线程中创建As...

  • 深入理解AsyncTask——线程池

    由于经常遇到AsyncTask,感觉功能比较简单,不成想过总结一下,现在感觉自己还是不要太懒,逼自己写下,总结一下...

  • 深入理解Android之AsyncTask

    为什么需要工作者线程 我们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操作的重任...

  • 深入理解AsyncTask的工作原理

    一、为什么需要工作者线程 我们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操作的...

  • AsyncTask的使用限制

    本文总结自Android实战技巧:深入解析AsyncTask AsyncTask的两种执行方式 execute(P...

  • AsyncTask异步任务类

    目录介绍 01.先看下AsyncTask用法 02.AsyncTask源码深入分析2.1 构造方法源码分析2.2 ...

  • 详细解读AsyncTask的黑暗面以及一种替代方案

    基于最新版本的AsyncTask详细解读主要是一些AsyncTask的原理解读,本篇将分析AsyncTask使用的...

  • AsyncTask深入解读(下)

    前言 在上一节AsyncTask深入解读(上)中已经介绍了AsyncTask的基本知识,本节将从源码出发,深层次分...

网友评论

      本文标题:深入理解AsyncTask

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