美文网首页
AsyncTask源码分析及个人理解

AsyncTask源码分析及个人理解

作者: 大宝京湘玉 | 来源:发表于2019-03-10 19:36 被阅读0次

### AsyncTask源码分析及个人理解

#### PS.阅读源码是开发者必备的能力,既然是能力那么就可以不断培养,逐渐增强的。

####一:整体概括

       1.我们都知道AsyncTask在执行过程中的四个常用的方法:

```java

@MainThread

protected void onPreExecute() {}

@WorkerThread

protected abstract Result doInBackground(Params... params);

@SuppressWarnings({"UnusedDeclaration"})

@MainThread

protected void onProgressUpdate(Progress... values) {}

@SuppressWarnings({"UnusedDeclaration"})

@MainThread

protected void onPostExecute(Result result) {}

```

上述的四个方法,

onPreExecute是在task执行之前调用的。

doInBackground是在执行过程中调用,可以看到方法上标注的执行线程,这在工作线程中被执行。

onProgressUpdate是在task执行过程中,反映执行的进度的。执行的线程可以看出是在主线程中。

onPostExecute这个方法是task在执行完耗时任务之后,将结果回调的方法,可以看出这个方法也是在主线程中被调用的。

     2.上述方法上标注的执行线程。有主线程 也有工作线程。在安卓开发过程中大家都知道安卓是单线程模式,也就是说安卓在执行过程中是在主线程中这个线程去处理关键任务,其他耗时操作应该放到子线程中去做。那么咱们就分析下这些线程是如何实现,以及怎么调用的。

在一开始查看AsyncTask源码时,对于顶部的实现就有线程池的生声明。注意,这些声明的变量时静态的,也就是说这是类共用的。

```java

//获取当前可用的cpu数量,这里有个疑问,如果正在执行任务的时候cpu忙碌,是不是会取到0?这是个问题

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

//这个就是针对上边的这个疑问的兜底,反正至少有2个cpu可供使用

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT -1, 4));

private static final int MAXIMUM_POOL_SIZE =CPU_COUNT *2 +1;

private static final int KEEP_ALIVE_SECONDS =30;

//定义的线程池,真正干活就是下边定义的线程池

public static final ExecutorTHREAD_POOL_EXECUTOR;

static {

ThreadPoolExecutor threadPoolExecutor =new ThreadPoolExecutor(

CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,

            sPoolWorkQueue, sThreadFactory);

    threadPoolExecutor.allowCoreThreadTimeOut(true);

    THREAD_POOL_EXECUTOR = threadPoolExecutor;

}

//虽然这里又重新定义了Executor,但是内部调用还是上边声明的THREAD_POOL_EXECUTOR,除非是单独再设定执行的线程池。不过一般使用很少这么用吧。

public static final ExecutorSERIAL_EXECUTOR =new SerialExecutor();

private static class SerialExecutorimplements Executor {

final ArrayDequemTasks =new ArrayDeque();

    RunnablemActive;

    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必须在主线程中声明,并且必须在主线程中调用。为什么会有这样的要求,因为内部实现使用的handler就是执行线程中的looper,如果在子线程中执行,会直接报错,因为AsyncTask已经做了检查。

3.准备工作已经做好了,那么去看看如何调用:

```java

//在定义一个task之后,真正调用的方式如下:

task.execut();

可以看到内部的实现具体调用方法是

@MainThread

public final AsyncTaskexecuteOnExecutor(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()方法已经被调用了,因为当前是在主线程,所以方法调用也是在主线程中的。

    onPreExecute();

    mWorker.mParams = params;

//下边就是真正执行任务的地方了  exec是excutor,执行的参数是runnable.mFuture这个参数是在初始化的时候创建的,具体创建 下边分析。

    exec.execute(mFuture);

return this;

}

//这是创建AsyncTask的时候 创建的mFuture,可以看出,他是对Runnable的包装。

public AsyncTask(@Nullable Looper callbackLooper) {

mHandler = callbackLooper ==null || callbackLooper == Looper.getMainLooper()

?getMainHandler()

:new Handler(callbackLooper);

    mWorker =new WorkerRunnable() {

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

            }

}

};

可以看到 当一个任务被放到线程池中执行。调用的是mFuture持有的mWorker的call方法。这个方法内部就是真正执行耗时操作。

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;

}

可以看到doInBackground方法在此被调用。而此时线程是子线程。也就是我们常用的编写耗时操作的地方。当这个操作执行完之后,回调用postResult方法,这个方法是使用handler发送一个消息,因为handler是在主线程中创建,所以这个handler处理消息当然也就在主线程中了。这个消息没有直接调用onPostResult方法,而是调用了AsyncTask的finish方法。这个方法如下:

private void finish(Result result) {

if (isCancelled()) {

onCancelled(result);

    }else {

onPostExecute(result);

    }

mStatus = Status.FINISHED;

}

会根据是否取消来调用对于的方法。正常来讲,调用onPostExecute方法,将结果告知被调用放。此次任务完成。

```

4.上述分析的只是一个task被执行完成。对于上述的线程池来讲,因为是静态的,所以使用方是所有的对象共用。也就是说创建多个task任务来执行,他们会被封装成runnable来保存在队列中一个一个被执行。在AsyncTask类的说明中可以看到,这个类是在执行小的耗时不长的任务比较高效,如果是计算型任务,或者是耗时长的操作,还是建议使用线程池来搞。

5.对于源码分析以及个人的思考,源码在某种程度上绝对是十分高效的。相对绝大多数的开发者来讲,使用源码或者针对源码的封装在性能上绝对是可靠的。学习源码可以让我们看到设计者的思想,和他们使用技术。对于日常开发很有帮助。

相关文章

  • AsyncTask源码分析及个人理解

    ###AsyncTask源码分析及个人理解 #### PS.阅读源码是开发者必备的能力,既然是能力那么就可以不断培...

  • AsyncTask源码分析

    前言 IntentService使用及源码分析 HandlerThread源码分析 AsyncTask使用及封装实...

  • AsyncTask 使用及封装实践

    前言 IntentService使用及源码分析 HandlerThread源码分析 AsyncTask使用及封装实...

  • 4.AsyncTask使用,原理

    资料 AsyncTask处理机制详解及源码分析-工匠若水 AsyncTask 源码解析-鸿洋 带你认识不一样的As...

  • Android源码解析-Asynctask

    android源码分析-AsyncTask 我们一般创建一个AsyncTask的任务代码如下: 下面开始分析源码:...

  • AsyncTask异步任务类

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

  • AsyncTask使用及源码分析

    AsyncTask其实干嘛用的,大家都明白,这里我就不多说了,今天主要是分析它的源码,然后了解我们实际使用过程会有...

  • AsyncTask使用及源码分析

    AsyncTask AsyncTask使用注意事项: AsyncTask只能被执行(execute方法)一次,多次...

  • AsyncTask使用及源码分析

    如需转载请评论或简信,并注明出处,未经允许不得转载 目录 前言 多线程在Android开发中非常常见,但是,你还在...

  • Android - AsyncTask (SDK 24)

    利用AsyncTask给ListView异步加载图片 实现图文混排 链接 AsyncTask源码分析 知乎

网友评论

      本文标题:AsyncTask源码分析及个人理解

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