Android多线程(AsyncTask篇)

【齐天的博客】转载请注明出处(万分感谢!):
https://blog.csdn.net/qijinglai/article/details/80729014

关联文章:
Android多线程(Handler篇)
Android多线程(AsyncTask篇)
Android多线程(HandlerThread篇)
Android多线程(IntentService篇)

老习惯先上一张流程图:
这里写图片描述

前言

上一篇详细的分析了Handler的源码和工作原理,使用时需要分别在Thread和Handler中编写代码逻辑,AsyncTask是Android提供的类,由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。它对Thread和Handler进行了封装,使得代码更加统一,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可,但其流程有些繁琐。

AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。我们只需要实现这几个方法并编写内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result,如下:

public abstract class AsyncTask<Params, Progress, Result>{
    ...
  • Params,对应doInBackground的参数:表示用于AsyncTask执行任务的参数的类型
  • Progress,对应onProgressUpdate的参数:表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型
  • Result,对应onPostExecute的参数:表示任务全部完成后所返回的数据类型

注意

  1. AsyncTask的类必须在主线程加载
  2. AsyncTask对象必须在主线程创建
  3. execute()方法必须在主线程调用
  4. 不要再程序中直接调用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()
  5. 一个AsyncTask对象只能调用一次execute()
  6. 1.6之前是串行;1.6开始并行;3.0开始串行,但可以使用executeOnExecutor()变为并行。

具体原因会在后面分析源码时说道

使用

步骤:

  1. 新建内部类,继承AsyncTask
  2. 确认参数类型
  3. 重写onPreExecute(),doInBackground(String… params),onProgressUpdate(Object… values),onPostExecute(Long aLong)四个回调方法
  4. 在方法中编写逻辑代码

使用
这里没有考虑内存泄漏问题,内存泄漏会在以后文章中专门更新

public class Test extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyAsyncTask task = new MyAsyncTask();
        task.execute();//参数可以为地址等,传到doInBackground的参数中
    }

    private class MyAsyncTask extends AsyncTask<String, Object, Long> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //前期准备,比如设置显示进度条,按钮点击后不可用等
        }

        @Override
        protected Long doInBackground(String... params) {
            //1,耗时操作
            //2.将进度公布出去
            int result = 0;//设置进度
            publishProgress(result);
            return null;//可以写返回值
        }

        @Override
        protected void onProgressUpdate(Object... values) {
            super.onProgressUpdate(values);
            //由values设置进度
        }

        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            //耗时操作执行完毕,更新UI
        }
    }
}

使用就写这么多,具体使用并不是本文重点,接下来重点分析原理。

回调方法分析

首先看一下四个回调方法

  1. onPreExecute

    @MainThread //表示该方法是运行在主线程中
    protected void onPreExecute() {
    }

    在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,一般在里面show一个loading或显示个进度条,告知用户后台任务即将开始。

  2. doInBackground

    @WorkerThread //表示该方法是运行在单独的工作线程中
    protected abstract Result doInBackground(Params... params);

    它会在onPreExecute()方法执行完成后立即执行,因为其运行在非主线程中,座椅在该方法中执行耗时任务,不会阻塞UI线程。该方法接收Params泛型参数,参数params是Params类型的不定长数组,该方法的返回值是Result泛型,由于doInBackgroud是抽象方法,我们在使用AsyncTask时必须重写该方法。在doInBackground中执行的任务可能要分解为好多步骤,每完成一步我们就可以通过调用AsyncTask的publishProgress(Progress…)将阶段性的处理结果发布出去,阶段性处理结果是Progress泛型类型。当调用了publishProgress方法后,处理结果会被传递到UI线程中,并在UI线程中回调onProgressUpdate方法,下面会详细介绍。根据我们的具体需要,我们可以在doInBackground中不调用publishProgress方法,当然也可以在该方法中多次调用publishProgress方法。doInBackgroud方法的返回值表示后台线程完成任务之后的结果。

  3. onProgressUpdate
    当我们在doInBackground中调用publishProgress(Progress…)方法后,就会在UI线程上回调onProgressUpdate方法

    @MainThread//表示该方法是运行在主线程中
    protected void onProgressUpdate(Progress... values) {
    }
  4. onPostExecute

    @MainThread//表示该方法是运行在主线程中
    protected void onPostExecute(Result result) {
    }

    当doInBackgroud方法执行完毕后,就表示任务完成了,doInBackgroud方法的返回值就会作为参数在主线程中传入到onPostExecute方法中,这样就可以在主线程中根据任务的执行结果更新UI。

原理分析

按照使用流程,两步:实例化和execute,所以我们先看构造方法:

    /**AsyncTask提供了3个构造方法,但最终都会调用参数最长的这个方法
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     * 必须在UI线程上调用此构造函数创建一个新的异步任务。
     * @hide
     */
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

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

mHandler后面会说到,这里先不提。所以这里只是初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象,这两个变量会暂时保存在内存中,稍后会用到它们。

之后再看一看execute()方法的源码:

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

所以它实际调用的是executeOnExecutor方法,传入了sDefaultExecutor参数,先看看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)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

首先判断状态,如果是不是等待状态(PENDING)就抛出异常,因为只能执行一次;若是等待状态就把状态改为运行状态(RUNNING),然后执行onPreExecute(),由此说明 onPreExecute()是第一个执行的回调。然后exec.execute(mFuture),由execute()中可知exec是sDefaultExecutor对象,我们看一下这个东西:

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

可以看出sDefaultExecutor是由SerialExecutor()实例而来,所以实际调用的是SerialExecutor的execute方法,再看看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是使用ArrayDeque这个队列来管理Runnable对象的,ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后看mActive是不是空的,是空的就调用scheduleNext()在头部拿一个给它,也就是说每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果。如何并行执行呢?不使用默认的线程池,自己配置,比如:

Executor exec = new ThreadPoolExecutor(
            15,
            200, 
            TimeUnit.SECONDS, 
            new LinkedBlockingQueue<Runnable>()
            ); 
new MyAsyncTask().executeOnExecutor(exec);  

这样就可以使用我们自定义的一个Executor来执行任务,而不是使用SerialExecutor。上述代码的效果允许在同一时刻有15个任务正在执行,并且最多能够存储200个任务。

我们接着上面源码说,在SerialExecutor的execute方法中传入了一个Runnable对象,由之前的调用地点可知Runnable对象就是mFuture,所以此处实际调用的是FutureTask的run()方法:

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         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 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);
        }
    }

这里只需要看result = c.call(),由其构造函数:

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

可知实际上这个c就是最开始提到的mWorker对象,所以此处调用的是Callable的call()方法,这个方法在哪呢?它就在最开始构造函数里初始化mWorker的里面,我们单独拿出来看看:

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

try里面首先设置一下进程优先级,然后我们终于看到了第二个方法doInBackground,其结果在finally时传给了postResult方法里,看一下postResult:

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

到这就和上一篇联系起来了,这里使用getHandler()得到Handler对象发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象,这个getHandler联系到构造AsyncTask时最开始的代码:

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

若构造时传入了looper,若不为空且不是主线程的looper,就用looper构造一个Handler;否则getMainHandler()。这里联系到上一篇的Hander,Handler在初始化是绑定looper和looper构造时产生的MessageQueue,具体请看Android多线程(Handler篇)
到此在执行到getMainHandler()方法:

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

可以看出实际为InternalHandler类的对象(但这里需要注意,sHandler是一个静态对象,为了能够将执行环境切换到主线程就一定要求这个对象是在主线程创建的。由于静态成员在加载类的时候初始化,因此就变相的要求AsyncTask的类必须在主线程加载)

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

handleMessage中对消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。那么finish()方法的源码如下所示:

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

到这里看到了onPostExecute,四个方法中三个已被我们找到。可以看到,如果当前任务被取消掉了,就会调用onCancelled()方法,如果没有被取消,则调用onPostExecute()方法,这样当前任务的执行就全部结束了。

若果是MESSAGE_POST_PROGRESS消息则会调用onProgressUpdate方法,至此四个方法全部找到。还差一个在doInBackground中发送进度时调用的publishProgress方法:

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

这就很简单了,和上面一样发送了消息给主线程。
所以说到底,AsyncTask也是使用的Handler异步消息处理机制,只是做了一个封装而已。

到此关于AsyncTask的重要内容就分析完了,欢迎指教谢谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qi T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值