Andrroid 线程(一) AsyncTask

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_29874521/article/details/82109111

主线程和子线程

主线程是指进程所拥有的线程,在 Java 中默认情况下一个进程只有一个线程,这个线程就是主线程,主线程主要处理界面交互的相关流程,因为用户随时会和界面发生交互,因此主线程在任何时候都必须有较高的响应速度,否则就会产生一种界面卡顿的感觉。为了保持较高的响应速度,这就要求主线程中不能执行耗时操作,这个时候子线程就派上用场了,子线程也叫工作线程,除了子线程以外的线程都是子线程。

Andrroid 沿用了 Java 的线程模型,其中的线程也分为主线程和子线程,其中主线程也叫 UI 线程。主线程主要是运行四大组件以及处理他们和用户的交互,而子线程为了执行耗时操作,比如网络请求,I/O 操作等。从 Andrroid 3.0开始,要求网络请求必须在子线程中执行,否则网络请求就会跑出 NewworkOnMainThreadException 异常,这是为了避免主线程由于耗时操作所阻塞从而出现 ANR 现象。

 

————————————————我是分割线————————————————

 

Andrroid 中的线程形态

Andrroid 中的线程,除了传统的 Thread 以外,还有 AsyncTask,HandlerThread 以及 IntentService,这三者的底层实现也是线程,但是他们具有特殊的表现形式,同时在使用上也各有优缺点。为了简化在子线程中访问 UI 的过程,系统提供了 AsyncTask,AsyncTask 经过几次修改,导致了对于不同的 API 版本 AsyncTask 具有不同的表现形式,尤其是在多任务的并发执行上。由于这个原因,很多开发者对 AsyncTask 的使用上存在误区,下面将详细介绍 AsyncTask 在使用时的注意事项,并从源码的角度分析 AsyncTask 的执行过程。

1.AsyncTask

AsyncTask 是一种轻量级的异步任务类,它可以在线程中执行后台操作,然后吧执行的进度和最终的结果传递给主线程并在主线程中更新 UI。从实现上来说,AsyncTask 封装了 Handler 和 Thread,通过 AsyncTask 可以更加方便的执行后台任务以及在主线程中访问 UI。但是 AsyncTask 并不适合特别耗时的后台操作,对于特别耗时的任务,建议使用线程池。

AsyncTask 是一个抽象的泛型类,它提供了 Params,Progress 和 Result 三个泛型参数,其中 Params 表示参数的类型,Progress  表示后台任务执行进度的类型,而 Result 表示后台任务执行结果的返回类型,如果 AsyncTask 确实不需要传递具体的参数,那么这三个泛型参数可以使用 void 代替。AsyncTask 这个类的声明如下:

public abstract class AsyncTask<Params, Progress, Result> 

AsyncTask 提供了四个和新方法,他们的含义如下:

(1)onProExceute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可用于做一些准备工作。

(2)doInBackground(Parmas...parmas),在线程池中执行,此方法用于执行异步任务,parmas 表示异步任务的输入参数。此方法可以通过 publicProgress 来更新任务的执行进度,publicProgress 方法会调用 onProgressUpdate 方法。另外此方法需要返回计算结果给 onPostExceute 方法。

(3)onProgressUpdate(Params...values),在主线程中执行,当后台任务的执行进度发生癌变时会调用此方法。

(4)onPostExceute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中 result 参数是后台任务的返回值,即 doInBackground 的返回值。

上面几个方法,onProExceute 先执行,戒指是 doInBackground,最后才是 onPostExceute,除了上述四个方法外,AsyncTask 还提供了 onCancelled 方法,它同样在主线程执行,当异步任务取消时,onCancelled 方法会被调用,这个时候 onPostExceute 则不会被调用,下面提供一个典型的实例,如下所示:

public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

    @Override
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        Long totasSize = 0;
        for (int i = 0; i < count; i++) {
            totasSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) (i / (float) count) * 100);
            if(isCancelled()) {
                break;
            }
        }
        return totasSize;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        setProgressPercent(values[0])
    }

    @Override
    protected void onPostExecute(Long aLong) {
        showDialog("Downloaded" + aLong + "bytes");
    }

在上面的代码中,实现了一个具体的 AsyncTask类,这个类主要用于模拟文件的下载过程,它的输入参数类型为 URL,后台任务进度类型为 Integer,后台任务的返回结果为 Long类型。注意到 doInBackground 和 onProgressUpdate 方法中他们的参数都包含 ... 的字样,在 Java 中 ...表示参数的数量不定,它是一种数组性参数,...的概念和 C 语言中的 ... 是一样的。当腰执行下载任务时,可以通过如下方法来完成:

        new DownloadFilesTask().execute(val1, val2, val3)

在 DownloadFilesTask 中,doInBackground 方法来执行具体的下载任务并通过 publicProgress 方法来更新下载进度,同时还需要判断任务是否被取消了。当下载任务完成后,doInBackground 会返回结果,即下载总字节数。需要注意的是,doInBackground实在线程池中执行的。onProgressUpdate 用于更新界面中的下载进度。它运行在主线程,当 publicProgress 被调用时,此方法就会被调用,当下载任务完成后,onPostExceute 方法会被调用,它业运行在主线程中,这个时候我们就可以在界面上做出一些提示,比如弹一个 Dialog 提醒用户下载已经完成。

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

(1)AsyncTask 类必须在主线程中加载,这就意味着第一次记载 AsyncTask 必须在主线程,当然这个过程已经在 Android 4.1 及以上版本中已经被系统自动完成。 在 Android 5.0 的源码中,可以查看 ActivityThread  的 main 方法,它会调用 AsyncTask 的 init  方法,这就满足了 AsyncTask 类必须在主线程中加载这个条件了,。至于为什么说必须满足这个条件,在后面我们会对这个问题进行分析。

(2)AsyncTask 对象必须在主线程中创建。

(3)exceute 方法必须在 UI 线程调用。

(4)不要再程序中直接调用 onProExceute(),onPostExceute(),doInBackground() 和 onProgressUpdate方法。

(5)一个 AsyncTask 对象只能执行一次,即调用一次 exceute 方法,否则会报运行时异常。

(6)在 Android 1.6 之前,AsyncTask 是穿行执行任务的,Android  1.6 的时候 AsyncTask开始采用线程池里处理并行任务,但是从 Android 3.0 开始,为了避免 AsyncTask 所带来的并发错误,AsyncTask 又采用了一个线程来穿行执行任务。尽管如此,在 Android 3.0 以及后续的版本,我们仍可以通过 AsyncTask 的 exceuteOnExecutor 方法来并行地执行任务。

 

————————————————我又出来了————————————————

 

AsyncTask 的工作原理

为了分析 AsyncTask 的工作原理,我们从它的 exceute 方法开始分析,exceute 方法又会调用 exceuteOnExecutor 方法,他们的实现如下:

 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 全部在这个串行的线程池中排队执行,这个排队执行的过程后面会进行分析。在 exceuteOnExecutor 方法中,AsyncTask 的 onProExecute 方法最先执行,然后线程池开始执行,下面分析线程池的执行过程,如下所示:

    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 的派队执行过程,首先系统会把 Params 参数封装成  FutureTask 对象,FutureTask是一个并发类,在这里它充当了  Runnable 的作用。如果有对 ArrayDeque 不理解的话,可以点击这里查看。接着 FutureTask 会交给 SerialExecutor 的 execute 方法处理,SerialExecutor 的 execute 方法首先会把 FutureTask 对象插入到任务对咧 mTasks 中,如果这个时候没有正常活动的 AsyncTask 任务,那么就会调用 SerialExecutor 的 scheduleNext 方法来执行下一个 AsyncTask 任务。同时当一个 AsyncTask 任务执行完成之后,AsyncTask 会继续执行其他的任务知道所有的任务都被执行位为止,从这一点看出,在默认情况下,AsyncTask 是串行执行的。从下面的源码可以更好的理解这段话的流程:

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    private final FutureTask<Result> mFuture;

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

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(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);
                }
            }
        };
    }

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

 

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

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(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;
    }

在上面的代码中,postRsult 方法通过 sHandler 发送了一个 MESSAGE_POST_RESULT 的消息,这个 sHandler 的定义如下:

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return 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 方法,到这里 AsyncTask 的整个工作过程就完成了。

 

猜你喜欢

转载自blog.csdn.net/sinat_29874521/article/details/82109111