AsyncTask详解

AsyncTask详解

实例代码:

private void doAsyncTask(){
    AsyncTask<Integer, Integer, Integer> asyncTask = new AsyncTask<Integer, Integer, Integer>() {
        @Override
        protected Integer doInBackground(Integer... params) {
            //在这里处理耗时事件 在后台的线程池中进行 异步的
            return null;
        }

        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            //这里进行UI通知  在主线程中进行
        }
    };
    asyncTask.execute(0);
}

AsyncTask说明

  1. 分析源码前,先说明,在 abstract class AsyncTask类中,常用元素均为static,所以说明了,但凡是使用AsyncTask方式进行异步处理,其提交的任务都由虚拟机系统统一管理每个进程的AsyncTask和处理。
  2. 在Android发展过程中,AsyncTask代码版本变化较大。早起引入时,AsyncTask实现方式是后台单一线程进行串行处理。从Android1.6开始,实现方式改变为使用了线程池允许多任务并行处理,但是在Android 3.0开始,任务默认任务执行为单线程,以避免多线程并行处理导致的程序错误。
  3. 如果你的确想使用并行处理,可以通过使用 executeOnExecutor(Executor exec, Params… params) 该函数进行多线程处理。

AsyncTask源码分析

  • execute(Params… params)

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

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        ...
    
        onPreExecute();  // 这里就执行了onPreExecute 在主线程中进行
    
        mWorker.mParams = params;  //传参
        exec.execute(mFuture);  //执行
    
        return this;
    }
    

    这里需要分析exec对应的sDefaultExecutor:

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

    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 是一个自定义的Executor,主要实现串行处理。 当executeOnExecutor->exec.execute(mFuture)执行的时候,对调用这里的execute方法,这个方法主要根据传入的Runnable–mFuture构造一个新的Runnable然后加入到mTasks队列的尾部。同时,判断当前系统是否有Task在执行,如果没有,调用scheduleNext 启动AsyncTask运行,处理mTasks队列中的Runnable…执行方式,通过ThreadPoolExecutor进行处理。这里要注意,虽然使用的是ThreadPoolExecutor,并且该线程池的容量不小,但是系统默认是使用的串行,单线程的。其控制原理在于上面所描述。每个Runnable执行完成后才会执行下一个 scheduleNext。

exec.execute(mFuture) 到底在做什么事情

从上面的分析可以看到程序走到SerialExecutor的scheduleNext逻辑上就走完了。但是,没有什么比较重要的,目前就执行了一个onPreExecute 在主线程中完成。经过分析,其重要的地方在于:
executeOnExecutor

    mWorker.mParams = params;  //传参
    exec.execute(mFuture);  //执行

execute传入了mFuture 已经对mParams进行赋值,后面的Runnable都是围绕着mFuture和mWorker进行,但是这两个成员是属于对象的,在一个对象的执行过程中很有可能被修改。所以,这个原因就会导致AsyncTask每个对象只能执行一次。为了保证这个,每个AsyncTask中保存了一个状态信息,系统通过mStatus来对状态进行控制。一旦mStatus == RUNNING 或者 FINISHED。。。就会抛出异常。并退出。

mFuture为何物

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            ...
                postResultIfNotInvoked(get());
            ...
        }
    };

其实mFuture就只是抛出了一个结果而已。应该也是隔回调,那么正在的调用在mWorker中,对于FutureTask的构造方法中会传入mWork

    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {  //mWorker继承了Callable接口 添加了call方法。
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //调整该线程的优先级
            //noinspection unchecked
            Result result = doInBackground(mParams); //调用doInBackground
            Binder.flushPendingCommands();
            return postResult(result);
        }
    };

通过上面分析,可以知道 exec.execute传入了一个FutureTask, FutureTask构造的时候传入了一个Callback 并且实现了Call函数。当mTasks队列中的一个Runable被取出执行的时候会调用run方法,改方法会调用到传入的mFuture中的也就是FutureTask中的run方法:

    public void run() {
        ...
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();  //调用到了call方法
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);  //如果异常 进程处理
            }
            if (ran)
                set(result);  //运行完进行结果处理
    }

该run方法调用到了mWorker中的Call方法。至此,就实现了在一个线程中执行doInBackground方法。
关于setException和set方法 都是设置运行结果后调用了finishCompletion();

private void finishCompletion() {
    ...
    done(); //调用了FutureTask的done方法,而这个方法刚好是之前mFuture中进行了定义

    callable = null;        // to reduce footprint
}

按之前所说 mFutureTask中的postResultIfNotInvoked进行了回调处理了结果。但是要注意,此时的调用环境还在后台线程中。所以,postResultIfNotInvoked函数中肯定使用了跨线程操作。

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();  //对结果进行了包装成Message然后通过handler进发送。
    return result;
}

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());  //这里使用了handler的另一种构造方法,所以重用了主进程的Looper...
    }

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

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

同时在doInBackground我们可以通过publishProgress进行状态跟新,实现如下,也是利用的主线程的Looper构造的Handler发消息进行异步处理。

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

如何使用多线程并行处理。

在google关于AsyncTask文档中说明了如果一定要使用多线程并行处理,那么应该使用executeOnExecutor传入一个线程池执行器。在上面的代码分析中,可以知道Executor是一个执行策略的定制,如下所述:

Executes the given command at some time in the future. The command
may execute in a new thread, in a pooled thread, or in the calling
thread, at the discretion of the {@code Executor} implementation.

所以,如果我们要使用多线程执行,那么就调用AsyncTask的execteOnExecutor函数,传入我们自定义的一个 Executor 代码如下:

private class MyExcutor implements Executor{
    @Override
    public void execute(Runnable command) {
        AsyncTask.THREAD_POOL_EXECUTOR.execute(command);
    }
}

MyExcutor excutor = new MyExcutor();
for (int i = 0; i < 10; i++){
    new MyAsyncTask().executeOnExecutor(excutor, i);
}

如此,就可以实现使用AsyncTask使用线程池进行并行处理。

关于AsyncTask线程池

public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1; //核心池大小
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;  //核心池最大数量
private static final int KEEP_ALIVE = 1;  
private static final BlockingQueue<Runnable> sPoolWorkQueue =  //队列最长128 如果超过就无法执行
        new LinkedBlockingQueue<Runnable>(128);

具体关于线程池的使用可查阅相关资料。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值