AsyncTask源码分析及个人理解

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容