美文网首页Android源码分析
菜鸟看源码之AsyncTask

菜鸟看源码之AsyncTask

作者: leilifengxingmw | 来源:发表于2019-12-13 09:33 被阅读0次

迁移自CSDN菜鸟看源码之AsyncTask

首先扯点别的:今天是第二天上班,看了看日历已经是2月23号了,2018年还剩9个多月。真是感觉人生如白驹过隙啊。今年自己竟然不知不觉已经26周岁了,趁着还算比较年轻(再过个3,4年这话就不好说了),努力奋斗!

今天梳理一下AsyncTask的源码

基于源码8.0

这部分文字叙述摘抄自郭霖老师的博客 Android AsyncTask完全解析,带你从源码的角度彻底理解

先看基本的使用方法:由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

  1. Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  2. Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  3. Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

继承AsyncTask通常需要重写下面几个方法

  1. onPreExecute():这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  2. doInBackground(Params...):这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
  3. onProgressUpdate(Progress...):当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
  4. onPostExecute(Result):当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

以下载一个文件为例

新建DownloadTask类继承AsyncTask

    static class DownloadTask extends AsyncTask<String, Integer, Boolean> {

        private final String TAG = getClass().getName();

        @Override
        protected void onPreExecute() {
            Log.d(TAG, "onPreExecute: showDialog");
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            String url = strings[0];
            try {
                while (true) {
                    int percent = doDownload(url);
                    //发布进度
                    publishProgress(percent);
                    if (percent >= 100) {
                        //下载完成
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            int progress = values[0];
            //设置进度
            Log.d(TAG, "onProgressUpdate: progress=" + progress);
        }

        @Override
        protected void onPostExecute(Boolean success) {
            Log.d(TAG, "onPostExecute: dismissDialog");
            if (success) {
                Log.e(TAG, "success");
            } else {
                Log.e(TAG, "failed");
            }
        }

        //模拟下载的方法
        private int doDownload(String url) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        }
    }

使用

private DownloadTask downloadTask;
downloadTask = new DownloadTask();       
downloadTask.execute("http://blog.csdn.net/u011240877/article/details/72905633");

如果要取消正在执行的任务

if (!downloadTask.isCancelled()) {
    downloadTask.cancel(true);
}

源码分析

首先先看一下AsyncTask的构造函数,必须在主线程调用

public AsyncTask() {
    this((Looper) null); 
}
public AsyncTask(@Nullable Handler handler) {
    this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
        //注释1处
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        //注释2处
        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;
            }
        };
        //注释3处
        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);
                }
            }
        };
    }

在注释1处,为mHandler赋值,mHandler是一个Handler类型的对象。因为我们传入的Looper对象是null,所以会调用getMainHandler方法为mHandler赋值。

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            //传入的Looper对象是主线程的Looper对象
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

方法返回的是一个InternalHandler对象。并且返回的InternalHandler对象是和主线程的Looper对象关联的。这也是为什么onProgressUpdate方法和onPostExecute方法在主线程执行的原因。

在注释2处为mWorker赋值,mWorker是一个WorkerRunnable对象。WorkerRunnable是AsyncTask的静态内部类,它是一个抽象类。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    //保存要执行的任务的参数
    Params[] mParams;
}

注意WorkerRunnable是实现了Callable接口的。当WorkerRunnable被执行的时候,它的call方法会被调用并返回执行结果。

在注释3处使用mWorker对象为mFuture赋值,mFuture是一个FutureTask对象。

 public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    //开始FutureTask的状态是NEW
    this.state = NEW;       
}

好了,构造函数先看到这里,接下来我们看一看AsyncTask的 execute方法。

//execute方法要在主线程调用
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
     //内部调用executeOnExecutor方法
    return executeOnExecutor(sDefaultExecutor, params);
}

调用executeOnExecutor方法

@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)");
                }
            }
            //AsyncTask的任务状态初始化为RUNNING
            mStatus = Status.RUNNING;
            
            //注释1处
            onPreExecute();
            //注释2处
            mWorker.mParams = params;
            //注释3处执行任务
            exec.execute(mFuture);
            //返回AsyncTask对象本身用于取消任务
            return this;
        }    
    

在上面的代码中可以看到,注释1处最先执行的是 onPreExecute 方法。
然后在注释2处为mWorker的mParams赋值。

注释3处,这里要注意一下,开始执行任务 exec.execute(mFuture);

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

当我们调用AsyncTask的execute(Params... params)方法的时候,内部会调用executeOnExecutor(Executor exec, Params... params)方法,传入的exec参数就是AsyncTask默认的 sDefaultExecutor

我们也可以直接调用AsyncTask的executeOnExecutor(Executor exec, Params... params)方法并传入我们自定义的一个Executor。就像下面这样:

Executor exec = new ThreadPoolExecutor(15, 200, 10,
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//传入自定义的Executor
new DownloadTask().executeOnExecutor(exec, "downloadUrl");

本篇文章只讨论使用AsyncTask默认执行器的情况。那么接下来我们看看AsyncTask的默认执行器sDefaultExecutor是何方神圣。

//注释1处
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//按顺序线性执行任务的执行器
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

注释1处注意一下,sDefaultExecutor是一个静态变量,也就是说应用中的所有AsyncTask实例,都会共用这一个Executor。

SerialExecutor类

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //注释1处将任务加入到mTasks
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //注释3处
                        scheduleNext();
                    }
                }
            });
            //注释2处
            if (mActive == null) {
                //开始执行任务
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                //注释4处
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

当我们调用 exec.execute(mFuture);的时候,
首先在注释1,处会把传入的mFuture加入到SerialExecutor的任务队列中。
然后注释2处,如果SerialExecutor中没有正在执行的任务,就调用scheduleNext方法来从任务队列中取出一个任务,如果任务不为null,就执行该任务。
注释3处,当一个任务执行完毕以后,然后再调用scheduleNext方法执行下一个任务,从这里我们可以看出AsyncTask默认是顺序执行任务的,每次只有一个任务被执行。

注释4处,使用THREAD_POOL_EXECUTOR来执行任务.。这个THREAD_POOL_EXECUTOR是个什么东东,了解一下。

AsyncTask类部分代码

//核心线程池数量
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;
//线程池的任务队列,限制长度为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

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

好了,现在我们知道了THREAD_POOL_EXECUTOR就是一个线程池,负责为我们创建线程来执行任务。当我们调用 THREAD_POOL_EXECUTOR.execute(mActive);时候,会从线程池中取出一个线程执行mActive的run方法。

SerialExecutor的execute方法

public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        //调用传入的Runnable对象的run方法
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

当mActive的run方法执行的时候,会调用传入的Runnable对象的run方法。在上面的分析中可以知道我们传入的Runnable对象r就是mFuture,是一个FutureTask对象。

接下来看一下mFuture的run方法

public void run() {
        //...
        try {
            //这个callable就是我们传入的mWorker对象。
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //注释1处
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                //注释2处
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

在mFuture的run方法的注释1处调用了我们传入的mWorker对象的call方法。再贴一下mWorker对象赋值的代码。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //注意,这里我们把mTaskInvoked设为了true
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //注释1处
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //注释2处
                    postResult(result);
                }
                //注释3处
                return result;
            }
        };

在mWorker的call方法中首先把mTaskInvoked设为了true,然后在注释1处,首先调用了doInBackground开始执行任务。然后在注释2处把执行结果post了出去。我们看一下AsyncTask的postResult方法。

private Result postResult(Result result) {
        //发送MESSAGE_POST_RESULT类型的消息
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

在postResult方法内部先调用getHandler获取mHandler对象,然后使用mHandler发送MESSAGE_POST_RESULT类型的消息。在上文的分析中我们知道mHandler是一个InternalHandler对象,我们看一下InternalHandler这个类。

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:
                    // 调用AsyncTask的finish方法。
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

在InternalHandler的handleMessage方法中,如果发送的消息类型是MESSAGE_POST_RESULT的话就调用AsyncTask的finish方法。

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        //将任务的状态置为FINISHED
        mStatus = Status.FINISHED;
    }

在方法内部,如果任务被取消了,就调用onCancelled方法,否则就调用onPostExecute方法。最后还要把AsyncTask的状态置为FINISHED。其实到这里对用户来说,执行已经结束了。

我们回到在mWorker的call方法的注释3处,返回了执行结果。

然后继续回到接下来看一下mFuture的run方法的注释2处

if (ran)
     set(result);

FutureTask的set方法

protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            //将FutureTask的状态置为NORMAL
            U.putOrderedInt(this, STATE, NORMAL); // final state
            //调用finishCompletion方法
            finishCompletion();
        }
    }
 private void finishCompletion() {
        //...
        //调用done方法
        done();
        //将callable置为null
        callable = null;        
    }

我们看到在finishCompletion方法中调用了done方法,我们在初始化mFuture的时候覆盖了done方法,我们看一下。

 mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                //注释1处
                    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);
                }
            }
        }; 

在上面代码中的注释1处首先调用了FutureTask的get方法获取执行结果,然后调用postResultIfNotInvoked方法。

AsyncTask的postResultIfNotInvoked方法

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

因为在mWorker的call方法中我们把mTaskInvoked设为了true,所以这里是不会执行的。到此整个分析过程就结束了。

总结:AsyncTask内部还是通过Handler机制来实现线程间通信:内部使用一个Executor执行提交的任务,当需要发布任务的执行进度和返回执行结果的时候,就是用一个主线程的Handler发送消息,然后在主线程进行UI改变。

参考链接

相关文章

网友评论

    本文标题:菜鸟看源码之AsyncTask

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