AsyncTask

作者: 书文换酒钱 | 来源:发表于2018-12-27 10:49 被阅读0次

    AsyncTask出现的契机

    线程的创建和销毁都会有开销,如果在进程中频繁的创建和销毁线程,是不可取的。应该采用线程池,可以避免因为频繁创建和销毁线程所带来的系统开销。
    Java线程池分析

    AsyncTask是什么

    AsyncTask是Android封装的一个轻量级的异步类,可以在线程池中执行异步任务,并可以将执行进度和结果传递给UI线程。
    AsyncTask的内部封装了两个线程池(SerialExecutorTHREAD_POOL_EXECUTOR)和一个Handler
    其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,Handler用于从工作线程切换到主线程。

    AsyncTask的范型参数

    public abstract class AsyncTask<Params, Progress, Result>{}
    #Params:开始异步任务执行时传入的参数类型;
    #Progress:异步任务执行过程中,返回下载进度值的类型;
    #Result:异步任务执行完成后,返回的结果类型;
    #如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。
    

    AsyncTask的核心方法

    @MainThread
    protected void onPreExecute() {}
    #此方法在doInBackground方法之前执行,在主线程中,用于进行界面初始化操作
    
    @WorkerThread
    protected abstract Result doInBackground(Params... params)
    #此方法在WorkerThread执行,用来处理耗时任务,此方法内不能更新UI
    
    @MainThread
    protected void onProgressUpdate(Progress... values)
    #当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,
    #方法中携带的参数就是在后台任务中传递过来的。
    #在这个方法中可以对UI进行操作,在主线程中进行
    #利用参数中的数值就可以对界面元素进行相应的更新。
    
    @MainThread
    protected void onPostExecute(Result result)
    #当doInBackground(Params…)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。
    #返回的数据来进行一些UI操作,在主线程中进行
    

    上面几个方法的调用顺序:
    onPreExecute() –> doInBackground() –> publishProgress() –> onProgressUpdate() –> onPostExecute()

    @MainThread
    protected void onCancelled(Result result) {
        onCancelled();
    } 
    #在主线程中执行,当异步任务取消时,onCancelled()会被调用,
    #这个时候onPostExecute()则不会被调用
    

    AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

    AsyncTask的使用

    继承AsyncTask,重写上面的几个方法,new TestTask().execute()
    使用注意事项:

    • 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。execute(Params… params)方法必须在UI线程中调用
    • 不能在doInBackground(Params… params)中更改UI组件的信息。
    • 一个任务实例只能执行一次,如果执行第二次将会抛出异常。
    • 内存泄漏,静态内部类持有外部类的引用。如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
    • 生命周期:activity distort 进行回收cancel()

    AsyncTask源码分析

    我们先来看AsyncTask的构造方法:

    #构造参数 looper
    public AsyncTask(@Nullable Looper callbackLooper) {
            # 此处对参数进行了判断,如果looper为空或者为UI线程的looper
            # 则走getMainHandler()方法,创建一个InternalHandler,
            # InternalHandler,主要用来处理AsyncTask结果的
            # 否,在直接创建一个Handler,此handler为looper所在线程的handler
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
          
            # mWorker实际上是一个Callable对象
            # mWorker的call方法,执行了耗时操作doInBackground(mParams);
            # 执行完毕后会调用postResult(result),传递给内部的Handler跳转到主线程中。
            # 此处只是实例化 mWorker对象,并没有调用call()方法
            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实际是一个FutureTask对象,FutureTask实现了Runnable接口
            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);
                    }
                }
            };
        }
    

    mFuture对象是怎么加载到线程池中,进而执行的呢? 我们就来看看execute()方法的源码

    @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) {
        # ...此处省略,如果Status不对抛异常的代码
        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = params;
        # 具体执行耗时任务是在exec.execute(mFuture)方法中.......
        exec.execute(mFuture);
        return this;
    }
    

    exec.execute(mFuture),exec默认是SerialExecutor,那我们来瞅瞅SerialExecutor

    private static class SerialExecutor implements Executor {
            final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
            Runnable mActive;
            public synchronized void execute(final Runnable r) {
    
              # 第一步,向队列中加入一个新的任务,即之前实例化后的mFuture对象。
                mTasks.offer(new Runnable() {
                    public void run() {
                        try {
                            r.run();
                        } finally {
                            scheduleNext();
                        }
                    }
                });
    
                # 第二步,调用 scheduleNext()方法,调用THREAD_POOL_EXECUTOR执行队列头部的任务
                if (mActive == null) {  scheduleNext(); }
            }
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
      }
    

    SerialExecutor 是个静态内部类,SerialExecutor 内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的。
    最终是由THREAD_POOL_EXECUTOR来执行任务的,瞅一眼

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

    THREAD_POOL_EXECUTOR其实是一个ThreadPoolExecutor线程池
    mFuture加入线程池中,然后执行mWorker的call方法 ( 执行doInBackground 方法,然后执行postResult方法 )

    瞅一瞅,AsyncTask是如何从Work线程将结果返回给UI线程的

    postResult方法是关键:

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

    该方法向Handler对象发送了一个消息,上文提了一嘴,Handler是在AsyncTask构造方法中初始化的。那我们就来瞅一眼AsyncTask中的InternalHandler:

    # AsyncTask中默认的使用的UI线程中的looper,传递进来的是getMainLooper
    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;
                }
            }
        }
    

    在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即执行完了doInBackground()方法并传递结果,那么就调用finish()方法。

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

    如果任务已经取消了,回调onCancelled()方法,否则回调 onPostExecute()方法。如果收到的消息是MESSAGE_POST_PROGRESS,回调onProgressUpdate()方法,更新进度。
    InternalHandler是一个静态类,为了能够将执行环境切换到主线程,因此这个类必须在主线程中进行加载。所以变相要求AsyncTask的类必须在主线程中进行加载。

    相关文章

      网友评论

          本文标题:AsyncTask

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