Android AsyncTask 从源码角度解析其原理和用法

AsyncTask简介:

众所周知知道,Android UI是线程不安全的,如果要想在子线程中更新UI操作,必须使用Android的异步消息处理机制。当然我们自己可以实现一个Handler+Message消息处理机制来在子线程中更新UI操作。有时候觉得自己写这个异步消息处理机制很麻烦有木有??不过庆幸的是,Android 给我们实现了这么一套异步消息处理机制,我们直接拿来用就是了,从而
AsyncTask就诞生了。AsyncTask用于Android的异步消息 处理机制,来实现子线程和UI线程间的一些通信。

AsyncTask的基本用法:

官网给了个很好的例子:

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,在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

1.Params:

用于 doInBackground()后台执行任务的参数。

2.Progress:

用于后台执行任务进度的更新。

3.Result:

当任务执行完毕时,需要对结果进行返回,用于保存执行结果。

AsyncTask执行的步骤如下:

onPreExecute():在UI线程中执行,通常用于异步任务执行之前,初始化一些数据,比如:初始化进度条等。

doInBackground(java.lang.Object[]):在后台线程中执行,一般耗时任务都在这个方法里面实现,当然在这个方法里面我们必须返回执行结果,用于onPostExecute(java.lang.Object)步骤,将异步任务结果发布到UI线程中。在这个方法里我们可以选择性的调用publishProgress(java.lang.Object[])方法,用于将异步任务执行进度信息发布到UI线程中。

onProgressUpdate(java.lang.Object[]):在UI线程中执行,用于对后台任务执行进度信息的更新。只有在doInBackground(java.lang.Object[])方法中调用了publishProgress(java.lang.Object[])方法,才能拿到后台任务执行进度信息。

onPostExecute(java.lang.Object):在UI线程中执行,用于获取后台任务执行的结果。

AsyncTask的特征

1.异步任务的实例必须在UI线程中创建

2.execute(Params... params)方法执行必须在UI线程中调用

3.一个异步任务只能执行一个,不然会抛出“Cannot execute task:the task is already running.”异常。

4.不能在doInBackground 做更新UI的操作。

5.不要手动的去调用onPreExecute(),doInBackground(Params...
params),onProgressUpdate(Progress... values),onPostExecute(Result result)
这几个方法。

解析以上5点Why??等我们分析完整个源码再来回答这个问题吧。

AsycnTask的源码分析

1.execute(params)

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

这个方法只执行了一行代码,就是调用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;
    }

我们可以看到这里有个状态值的判断,就是枚举类型Status了,看看代码:

public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

三种状态用来标志当前的异步任务处于那种状态。当前任务的出事状态是PENDING:为待执行状态。

接着我们回到executeOnExecutor方法中

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

第一个任务执行,第 3 行的if条件不满足,那么第15行重新标记当前任务状态为正在执行。第17行调用 onPreExcute()方法,在UI线程中我们可以重写此方法用于执行异步任务之前的一些准备工作。第20行是exec线程池真正执行后台耗时任务mFuture的地方。mFuture就是实例化AsyncTask对象的时候获得。exec是外部传入的参数,该参数在实例化的时候默认是sDefaultExecutor顺序执行的线程池。

看代码你会发现,当同一个任务被执行两次的话,第 3 行的if条件就满足了,自然就会抛出相应的异常,这里就解释了AsyncTask特征的第3点,同一个任务不能被执行两次以上。

2.我们来看看AsyncTask实例化的代码

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

看着代码这么长,其实就初始化了两个变量,第一个变量是mWorker,第二个变量mFuture。

1.mWorker:是一个内部接口类对象,实现了里面的call()方法,call()方法里调用了

doInBackground(mParams)方法。何时调用mWorker.call()方法?我们发现 mWorker作为参数用作第二个变量mFuture的获取。
2.mFuture:是一个线程接口类对象,run()方法里面调用了mWorker.call()方法,具体怎么调用就不属于AsyncTask类的范围了。</span>
总结:其实实例化对象的时候只要记住我们持有了mFuture对象,里面回调了done()方法,回调里面仅仅是调用了postResultIfNotInvoked(get())方法。进入看看</span>
</pre><pre code_snippet_id="656316" snippet_file_name="blog_20150430_13_3699169" name="code" class="java">private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }


代码10-12行可以看出,这里用Handler+Message消息处理机制将 异步任务执行结果发布到UI线程中,看看sHandler怎么实现的吧?

 protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

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

    private static class InternalHandler extends Handler {
        @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;
            }
        }
    }

这是一个静态内部类,在获得不同消息做不同处理,MESSAGE_POST_RESULT:消息调用 mTask.finish()。代码在第 8 行实现了,该方法里面调用了

onPostExecute(result)

将异步执行任务发布到UI线程中,并且在第 14 行改变当前任务的执行状态为finished。  MESSAGE_POST_PROGRESS:消息调用了 mTask.onProgressUpdate().用于发布任务执行进度到UI线程中。

这里我们可以看出,AsyncTask 自己内部实现了一个 内部的 Handler + Message + Thread 消息处理机制用于执行耗时任务更新UI操作等。 我们会发现InternalHandler的申明实在类加载的时候就实现了,代码:

 private static final InternalHandler sHandler = new InternalHandler();

由于我们的Handler+Message消息处理机制是对用于UI线程的通知,基于消息是谁发送谁处理原理,我们的Handler必须是在UI线程中申明,也就是说,只有UI线程持有Handler的对象发送的消息才可以在UI线程中获取到这个消息。因此我们就可以解释AsyncTask特征的第1点:异步任务的实例必须在UI线程中创建。

至此 AsyncTask执行基本结束。

3.总结: 客户端调用execute()方法,先执行 onPreExecute()方法,然后通过线程池sDefaultExecutor 执行一个 mFuture异步任务,在这个过程中 调用doBackground方法执行后天任务,然后通过InternalHandler 消息处理机制将后台执行的结果通过 onPostExecute()方法发布给UI线程。因为onPreExecute()方法的运行是在后台任务执行前运行,所以 execute()必须在 UI线程中调用。这就解释了 AsyncTask特征的第二点:execute(Params...
params)方法执行必须在UI线程中调用。

注意:我么这里还有一个问题:最新版本的AsyncTask 是顺序执行异步任务的,也就是说只有第一个任务执行完成之后才能执行第二条任务,为什么会这样呢?还是从源码中找答案吧。AsyncTask 使用的默认线程池是 sDefaultExecutor,看代码实现:

 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static final InternalHandler sHandler = new InternalHandler();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    private volatile Status mStatus = Status.PENDING;

    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

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

AsyncTask 内部实现了一个顺序执行的线程池,那么如果我们想要并发执行异步任务该怎么办呢?不用着急, AsyncTask 给你留了一个后门,我们使用 AsyncTask 一般都是调用 execute(params)方法, 其实你会发现 execute 也是调用 executeOnExecutor方法的,只是参数使用的是默认sDefaultExecutor,所以我们可以 自己直接调用executeOnExecutor方法,然后自定义一个 并发的线程池作为参数传入。比例我们可以这么调用:

ExecutorService executorService = Executors.newCachedThreadPool();
        asyncTask.executeOnExecutor(executorService,params);

那么这个任务就是并发的了。

时间: 2024-11-06 03:49:45

Android AsyncTask 从源码角度解析其原理和用法的相关文章

Android之Handler源码深入解析

闲着没事,就来看看源码,看看源码的各种原理,会用只是简单的,知道为什么才是最牛逼的. Handler源码分析那,从使用的步骤来边用边分析: 1.创建一个Handler对象:new Handler(getMainLooper(),this); 这是我常用的一个方式,getMainLooper是获取主线程的Looper,this则是实现CallBack的接口 看一下Handler的构造函数 public Handler() { this(null, false); } public Handler(

(干货) Android Volley框架源码详细解析

前言 经常接触Android网络编程的我们,对于Volley肯定不陌生,但我们不禁要问,对于Volley我们真的很了解吗?Volley的内部是怎样实现的?为什么几行代码就能快速搭建好一个网络请求?我们不但要知其然,也要知其所以然,抱着这样的目的,本文主要详细讲述Volley的源码,对内部流程进行详细解析. Part 1.从RequestQueue说起 (1)还记得搭建请求的第一步是什么吗?是新建一个请求队列,比如说这样: RequestQueue queue = Volley.newReques

android动画之从源码角度分析动画原理

以前一直不懂android的动画机制,android系统是如何实现动画的,所以导致只会做一些android系统已经为我们封装好的动画即:AlphaAnimation, TranslateAnimation, ScaleAnimation,RotateAnimation以及这些动画混合起来使用,其实有android系统为我们提供的这几种动画是可以满足我们平时的基本需求了,但是要做一些高级的动画就是不可能的,比如3D动画,所以就看了一下android系统所带动画的源码,做了一些总结,下面就是我对动画

从源码角度解析Netty的React模式是如何工作的

Netty 支持多种实现方式,比如nio,epoll 等,本文以nio的实现方式进行讲解. 1.EventLoop : 事件循环看,简单来说就是一个死循环监听事件,如果事件来了,处理掉.通常做法就是开启一个独立线程,一直循环. 伪代码: while (queue.waitForMessage()) { queue.processNextMessage();} 2.EventLoopGroup: 一组(多个)事件循环. 3.bossGroup 和 workGroup EventLoopGroup

MySQL半一致性读原理解析-从源码角度解析

1.什么是半一致性读 A type of read operation used for UPDATE statements, that is a combination of read committed and consistent read. When an UPDATE statement examines a row that is already locked, InnoDB returns the latest committed version to MySQL so that

《Android源码设计模式解析》读书笔记——Android中你应该知道的设计模式

断断续续的,<Android源码设计模式解析>也看了一遍,书中提到了很多的设计模式,但是有部分在开发中见到的几率很小,所以掌握不了也没有太大影响. 我觉得这本书的最大价值有两点,一个是从设计模式的角度去理解Android源码,结合着日常开发中的常用类,对设计模式的理解会更加的深刻:另外一个好处就是了解常用模式,再看其他人写的代码的时候,更容易理解代码思路.下面是我的读书笔记和一些思考,设计模式只整理我认为重要的部分. 建造者模式 建造者模式最明显的标志就是Build类,而在Android中最常

Android 开源项目源码解析(第二期)

Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations 源码解析 SlidingMenu 源码解析 Cling 源码解析 BaseAdapterHelper 源码分析 Side Menu.Android 源码解析 DiscreteSeekBar 源码解析 CalendarListView 源码解析 PagerSlidingTabStrip 源码解析 公共

Android布局性能优化—从源码角度看ViewStub延迟加载技术

在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码中通过控制View.VISIABLE动态的更改它的可见性.这样的做法的优点是逻辑简单而且控制起来比较灵活.但是它的缺点就是,耗费资源,虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性.也就是说,会

从源码角度看Android系统SystemServer进程启动过程

copy frome :https://blog.csdn.net/salmon_zhang/article/details/93208135 SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动过程以及SystemServer进程做了哪些重要工作. 1. SystemServer进程启动的起点从<从源码角度看Android系统Zygote进程启动过