AsyncTask源码分析

1.AsyncTask基本使用

AsyncTask主要用来执行耗时操作,同时它把执行进度和结果传递给UI线程,因此很适合一些需要在执行完耗时操作后更新UI或者执行耗时操作过程在UI上显示进度的场景。

AsyncTask基本使用如下:

①首先自定义一个类继承AsyncTask

class MyAsyncTask extends AsyncTask<URL,Integer,Integer>{

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Integer doInBackground(URL... urls) {
        int totalSize = 0;
        for(int i=0;i<10;i++){
            //执行一个下载任务
            totalSize += Download.download(urls[i]);
            publishProgress(i/10);

            if(isCancelled)
               break;
        }
        return totalSize;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //更新显示下载进度的进度条
        updateProgress(values[0]);
    }

    @Override
    protected void onPostExecute(Integer results) {
        ToastUtils.showShort("共下载"+results);
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

②执行任务

new MyAsyncTask().execute(url1,url2);

AysncTask对象可以在主线程中创建,但是execute方法按照规范必须在主线程中执行。因为onPreExecute方法是在execute的线程中执行的。而构造函数内部的Handler则默认使用了主线程了Looper,所以AysncTask对象创建的线程不做要求。

主要有五个方法:

  • onPreExecute:主线程中执行,可以用来做一些执行耗时操作之前的准备工作。
  • doInBackground :在新线程中执行,必须实现的方法,用来做耗时操作。可以通过publishProgress方法调用onProgressUpdate显示下载进度。下载完成后会调用onPostExecute方法。
  • onProgressUpdate:主线程中执行,调用publishProgress才会触发此方法。
  • onPostExecute:主线程中执行,任务执行完成后会调用此方法。
  • onCancelled:主线程中执行,任务取消时被调用。

AsyncTask接受三个参数,第一个参数传递给doInBackground作为执行耗时操作的参数,第二个参数传递给onProgressUpdate作为进度更新值,第三个参数传递给onPostExecute作为执行结果返回。AsyncTask的使用很简单,接下来就深入内部了解下它是怎么工作的吧。

2.AsyncTask源码分析

从哪里使用就从哪里开始,显然我们从new MyAsyncTask().execute(url1,url2);中的execute方法作为入口,一探究竟。

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

execute方法没做啥,就是调用了executeOnExecutor。咦,有个不认识的家伙sDefaultExecutor,这是什么呢?看下定义sDefaultExecutor的地方:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

Executor是个线程池框架接口,sDefaultExecutor指向SERIAL_EXECUTOR,那SERIAL_EXECUTOR内部应该是实现线程池类似的功能,来看看SERIAL_EXECUTOR定义的地方:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

找到了SERIAL_EXECUTOR的实现类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);//ThreadPoolExecutor真正执行任务
        }
    }
}

它靠一个ArrayDeque集合存储Runnable对象,execute方法就是往集合中加入Runnable。mActive是当前正在执行的Runnable的对象,如果mActive == null表示当前没有正在执行的任务,就会调用scheduleNext方法,从mTasks中取出Runnable对象,再放入线程池THREAD_POOL_EXECUTOR中执行。THREAD_POOL_EXECUTOR的定义:

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_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;
}

ThreadPoolExecutor是线程池的真正实现,它的参数的含义这里就不多说了。所以可以看到AsyncTask内部有两个线程池,SerialExecutor和ThreadPoolExecutor,但是它们两个分工明确,SerialExecutor负责将任务一个一个存储起来,然后一次只取出一个任务放入到ThreadPoolExecutor中进行处理。SerialExecutor负责排队,ThreadPoolExecutor负责任务的真正执行。

回过头来看最初的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;
}

用mStatus记录了当前Task的状态,mStatus初始值是Status.PENDING,一旦调用了execute之后就会赋值为Status.RUNNING,所以execute方法只能执行一次。

params我们知道是传入的参数,mWorker.mParams又是啥?先来看看mWorker的定义:

private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;

其中WorkRunnable就是一个Callable对象,如下声明,FutureTask可以异步执行Callable对象的call方法,并且返回结果。因此是先把参数传入到Callable中,而Callable又会作为mFuture的执行体,再把mFuture放入线程池exec中执行。exec即是最开始传入的sDefaultExecutor,即SerialExecutor串行线程池排队。

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

mWorker和mFuture都是在AsyncTask的构造方法中定义的,来看看:

mWorker = new WorkerRunnable<Params, Result>() {
        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<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);
            }
        }
    };

doInBackground方法在mWorker的call方法中被调用了,因此它是执行在子线程中的。任务结束后会执行FutureTask的done方法,done中调用了postResultIfNotInvoked,我们来看看它里面做了什么:

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

执行完任务后调用了postResult方法,并且把result也传过去了,但是此时postResult还是执行在子线程中的,因此要用Handler把线程切换到主线程中,来看看postResult是怎么处理的:

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

postResult中发送了一条MESSAGE_POST_RESULT的Message,Message由getHandler方法得到的Handler进行处理。找到这个Handler新建对象的地方,也在AsyncTask的构造方法中:

 public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
    ……
 }

通常构造方法中不会传入Looper,因此会走getMainHandler方法,来看一下:

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

这里注意,InternalHandler用的是主线程的Looper,所以InternalHandler最终会在主线程中处理消息。

返回了一个InternalHandler对象,InternalHandler继承了Handler类:

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:
                // 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消息就会到这个InternalHandler中处理。然后调用了finish方法,看看该方法做了什么:

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

此时调用了onPostExecute,所以onPostExecute是在主线程中进行最后的结果处理的。

到这里AsyncTask的流程就走完了。AsyncTask内部就是两个线程池和一个Handler,SerialExecutor负责排队,ThreadPoolExecutor负责任务的真正执行,InternalHandler用于切换线程。

3.不同版本的AsyncTask的区别

Android1.6之前,AsyncTask是串行执行任务,

Android1.6之后并行处理,

Android3.0之后又变成了串行执行任务。Android3.0之后可以通过executeOnExecutor方法进行并行处理任务。

4.AsyncTask的缺陷

① AsyncTask并不会随着Activity或Fragment销毁而销毁。这就意味着AsyncTask的任务会一直执行直到任务结束。任务结束时,如果cancel方法调用了,则执行onCancelled方法,如果cancel方法没调用,则执行onPostExecute方法。

② 由于前面①中所说的AsyncTask在Activity销毁后还在继续执行任务,如果AsyncTask是非静态内部类,那么就会持有创建AsyncTask的Activity的引用,导致Activity无法回收,造成内存泄漏。

③ 当Activity由于屏幕旋转等原因导致重建时,AsyncTask会出现结果丢失的问题。Activity已经重建了,是一个新的对象,而AsyncTask中持有的引用还是之前的Activity,所以当AsyncTask执行完任务之后,onPostExecute没有任何作用。

购物商城项目采用PHP+mysql有以及html+css jq以及layer.js datatables bootstorap等插件等开发,采用了MVC模式,建立一个完善的电商系统,通过不同用户的不同需求,进行相应的调配和处理,提高对购买用户进行配置….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值