AsyncTask源码深入解析

最近想写一篇关于源码解析的文章,发现AsyncTask代码量不多,可里面的东西却是很值得学习的,所以故那这来“开刀”

首先作为Android开发者我们对于AsyncTask想必比大家都知道吗,大白话讲就是在后台执行耗时任务再把最终的结果返回主线程更新UI。如下代码就它的模板代码:

class MyAsyncTack extends AsyncTask<Void(传入参数),String(执行中阶段行结果),String(任务完成返回结果)>{
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //doInBackground执行前一些初始化的操作都在这里
    }

    @Override
    protected String doInBackground(Void... voids) {
        //后台耗时任务执行中。。。
        return null;
    }
    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        //后台执行的任务会发回一个或多个阶段性进度结果,这个是可以用来去更新交互页面。
    }
    @Override
    protected void onCancelled() {
        super.onCancelled();
        //在后台任务被取消时回调
    }
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        //耗时任务完成返回结果,刷新ui
    }
}
    
//执行AsycnTask
MyAsyncTack myAsyncTack=new MyAsyncTack();
myAsyncTack.execute();

这就是整的一个AsyncTask模板代码了。

从上面的模板中,整个过程是从myAsyncTack.execute();开始的,那我们就execute()开始吧 AsyncTask关于这部分的源码如下:

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

从上面的代码我们可以看出AsyncTask通过判断Status来防止AsyncTask多次执行,然后调用了onPreExecute();抽象方法,为任务的执行做一些准备和初始化操作。接着有三个对于现在我们来说很陌生的变量(mWorker,exec,mFuture).

对于mWorker,mFuture这两个变量在AsyncTask这个类初始化的时候就已经初始化了,对应的关键源码如下

public AsyncTask() {
    this((Looper) null);
}

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

public AsyncTask(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);
                    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);
                }
            }
        };
}

如上所示,mWorker很简单,就是AsycnTask里面的抽象内部类,实现了Callable接口,接下来就是mFuture即FutureTask,对于平时写业务层的我们对这个FutureTask类接触的还是比较少而又是这篇文章的重点,所以重点讲。

FutureTask

FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。概念总是难以理解,所以给你们一个关于使用FutureTask的demo,这个demo就是整个AsyncTask的核心。

public class FutureTest1 {

    public static void main(String[] args) {
        Task task = new Task();// 新建异步任务
        FutureTask<Integer> future = new FutureTask<Integer>(task) {
            // 异步任务执行完成,回调
            @Override
            protected void done() {
                try {
                    System.out.println("future.done():" + get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        // 创建线程池(使用了预定义的配置)
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(future);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        // 可以取消异步任务
//         future.cancel(true);
        try {
        // 阻塞,等待异步任务执行完毕-获取异步任务的返回值
            System.out.println("future.get():"+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    // 异步任务
    static class Task implements Callable<Integer> {
        // 返回异步任务的执行结果
        @Override
        public Integer call() throws Exception {
            int i = 0;
            for (;i<10;i++) {
                try {
                   System.out.println(Thread.currentThread().getName()+"_"+ i);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return i;
        }
    }

}

上面demo运行结果如下:

FutureTask.PNG

FutureTask可以获取异步任务线程的最终结果,所以future.get()返回10。

回到我们的AsyncTask里,同理如上面demo一样,会先执行mWorker的call()里面的方法,call方法里就执行抽象方法doInBackground(mParams);我们就这回到方法里执行耗时任务,然后拿到结果执行postResult(result)方法,而Future的get()也可以从Callable拿到执行的结果。

  • 分析exec.execute(mFuture); 首先exec我们要知道exec是什么?顺着他的源码向上找,代码如下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

这个sDefaultExecutor是什么呢,再顺着这推,关键源码如下:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

ArrayDeque是一个先进先出的队列存储Runnable对象,offer方法加到队尾,poll()从队头取,当运行的第一次的时候mActive就是空的,所以还是从mTask取出一个由THREAD_POOL_EXECUTOR执行,等下一次是mActive不为空就通过finally去执行 scheduleNext();方法,这段代码里有两个讲解点,分别是 r.run();和THREAD_POOL_EXECUTOR

  • 首先r.run()调用的是哪个方法呢 通过exec.execute(mFuture);我们知道上面的Runnable就是mFuture,即调用了mFuture.run();而这个源码如下:
private Callable<V> callable;

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

public void run() {
    if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

我已经把相关的代码都截出来的很详尽了,可以看出callable就是我们上面的mWorker变量,然后就是调mWorker.call()方法执行抽象方法doInBackground(),

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

private Result postResult(Result result) {
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

同时我上面FutureTask的demo也和这个形成了对应连贯,这就是我demo的底层实现,最后通过postResult();里的sHandler发送一个MESSAGE_POST_RESULT的消息 我们追踪先关代码:

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:
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }}
}

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

这里反而没什么好讲的,通过handler调用finish()方法,假如没有取消就是调用抽象方法onPostExecute(),假如取消了就调用抽象方法onCancelled();而当handler发送MESSAGE_POST_PROGRESS,就是调抽象方法onProgressUpdate(),很简单没什么说的。

  • 接着分析THREAD_POOL_EXECUTOR,相关源码如下:
 private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);
    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

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

其实这就是定义了一个线程池。同时运行线程数Math.max(2, Math.min(CPU_COUNT - 1, 4)),线程池总大小CPU_COUNT * 2 + 1;但是由于SerialExecutor的存在,它会强制串行并发,所以实际上只有一个线程在跑,所以也就不存在任务数超过线程池总大小的问题了。当然这是一个默认实现,我特也可以通过public static void setDefaultExecutor(Executor exec)进行更改。

到此整个分析过程就结束了,总结一下AcyncTask汇集了线程池,handler等相关知识,也告诉我们,不管表明多花俏,底层原理才是最重要的,懂得底层原理才能不变应万变,废话有点多了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CodingBlock

Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html

753
来自专栏逸鹏说道

.net采集网页方法大全(5种)

/// <summary>方法一:比较推荐 /// 用HttpWebRequest取得网页源码 /// 对于带BOM的网页很有效...

39917
来自专栏CodingBlock

Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html 一、AsyncT...

4457
来自专栏Phoenix的Android之旅

深入了解Android的Looper

Handler我们都知道,它需要和Looper绑定,当Handler在主线程创建,则会默认绑定主线程的Looper,当是在子线程创建,则需要在Handler的构...

801
来自专栏开发之途

Android多线程之HandlerThread源码解析

创建 HandlerThread 并调用 start() 方法,使其在子线程内创建 Looper 对象

1224
来自专栏开发之途

Android多线程之AsyncTask源码解析

1273
来自专栏三好码农的三亩自留地

Android AsyncTask实现原理和使用技巧分享

我们写App都有一个原则,主线程不能够运行需要占用大量CPU时间片的任务,如大量复杂的浮点运算,较大的磁盘IO操作,网络socket等,这些都会导致我们的主线程...

1403
来自专栏AnitDDoS

防御各种DDoS反射的方法

1430
来自专栏王磊的博客

HttpWebRequest模拟POST提交防止中文乱码

测试通过,请求的为自己写的一般处理程序,代码如下: Encoding myEncoding = Encoding.GetEncoding("gb2312"); ...

37610
来自专栏向治洪

android异步任务asyntask详解

在Android中实现异步任务机制有两种方式,Handler和AsyncTask。 Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Han...

2128

扫码关注云+社区

领取腾讯云代金券