AsyncTask详解

1,190 阅读5分钟

概述:

AsyncTask是一个轻量级的异步任务类,在线程池中串行执行任务,通过Handler把进度和执行结果发送到主线程,更新UI。

简单使用:

 MyAsyncTask mAsyncTask = new MyAsyncTask();
mAsyncTask.execute("params");
 
/**
* AsyncTask<Void, Integer, Boolean>
* 泛型参数分别为:后台任务执行的参数,更新进度的参数,任务执行完成的结果
*/
public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{
        private ProgressDialog mProgress;
        private int currentProgress;
        //任务开始前的准备操作,可以做些UI的准备或初始化。
        //多个任务时,无论是否等待,asyncTask.execute()的时候该方法就在主线程调用
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgress.setMax(100);
            mProgress.setMessage("下载进度");
            mProgress.show();
        }

        /**
        * 线程池中执行,不能更新UI
        **/
        @Override
        protected Boolean doInBackground(String... params) {
            while (currentProgress <= 100) {
                //想更新进度的话,主动调用该方法,会通过handler给onProgressUpdate方法。
                publishProgress(currentProgress);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                   return false;
                }
                currentProgress++;
            }
            return true;
        }

        //publishProgress通过handler调用,主线程
        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgress.setMessage("下载进度:"+values[0]);
        }

        //执行完成后的结果,主线程
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if (aBoolean) {
                mProgress.setMessage("下载完成");
            }else {
                mProgress.setMessage("下载出错");
            }

                mProgress.cancel();
        }
        
        //中断任务时,调用该方法。但并不一定会马上中断。
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

源码分析:

AsyncTask内部使用的是Callable、Future,在线程池中执行的原理

构造函数入手:
初始化:主线程Handler、callable、future

public AsyncTask() {
        // 主线程Handler
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // Callable,AsyncTask任务在这里执行
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //后台要执行的任务
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                } finally {
                    //任务执行完以后,通过msg在Handler中调用onPostExecute
                    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);
                }
            }
        };
    }

调用mAsyncTask.execute()方法:
将mFuture封装到Runnable中,放入队列,在线程池中执行
多个AsyncTask时,依次从队列中取出,串行执行

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

方法2:    
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //如果状态为正在运行或者已经结束,报错。只能调用一次mAsyncTask.execute()方法
        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;//改变状态
        //此时还是在主线程中,所以可以操作UI
        onPreExecute();
        mWorker.mParams = params;
        //Executor实现类
        exec.execute(mFuture);
        return this;
    }
    
    
    
    //上一步,将mFuture传入该类execute中
    private static volatile Executor sDefaultExecutor = new SerialExecutor();
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        //mFuture参数,也即是构造方法中的实例
        public synchronized void execute(final Runnable r) {
              //封装成一个Runnable,放到队列的最后。
语句1:        mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        //mFuture的run方法,执行Callable.call(),即mWork.call,进而调用doInBackground()(初始化中实现)
语句5:                 r.run();
                    } finally {
语句6:                 scheduleNext();
                    }
                }
            });
            //多个AsyncTask是串行执行:null时马上执行
            //上面finally中执行完以后,自动取出下一个任务执行
            if (mActive == null) {
语句2:          scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //取出队列中的Runnable放入线程池中执行
语句3:     if ((mActive = mTasks.poll()) != null) {
语句4:         THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

到这以后,AsyncTask基本上执行流程就完了:

  1. 构造方法中初始化handler、Callable、future
  2. asycnTask.execute将任务封装到Future --> Runnable,放入队列中
  3. 如果线程池当前没有任务,马上执行。如果有正在执行的任务mActive,则等待执行完以后再从队列中取出执行
  4. onPostExecute、onProgressUpdate方法的调用通过给主线程mHandler发送消息调用,在源码中可以轻易找到

串行、并行

  • 默认串行
  • asyncTask.executeOnExecutor(AsyncTask.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;
}
//默认Executor实现类,串行
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
    //串行逻辑
}


//调用asyncTask方法,默认使用串行实现类
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

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

所以,直接调用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "")
把future对象直接放入线程池中执行,并行
  • 调用asyncTask.execute方法时,内部调用executeOnExecutor使用的是默认Executor实现类,决定了串行执行
  • 我们可以调用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ""),直接使用线程池来处理,这样就可以并行执行了
  • "AsyncTask并不适合进行特别耗时的后台任务,对特别耗时的任务,建议使用线程池。"--开发艺术探索
    • 原因是因为:AsyncTask默认是串行执行的,如果是特别耗时任务,会影响其他AsyncTask任务迟迟得不到处理
    • 如果是特别耗时任务时,可在后台服务、计划任务、线程池中处理

cancle操作

<!--AsyncTask-->
public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}
<!--FutureTask-->
public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)  t.interrupt();
            } finally { // final state
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

//影响doInBackground以后调用哪个方法
private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
  • 不会立即中断任务
  • 如果cancle时正在执行,影响doInBackground以后调用哪个方法
  • 可在doInBackground中isCancelled或Thread.interrupted()判断来终止任务
  • doInBackground可能会引起InterruptedException,需做收尾处理工作

内存泄漏

页面销毁时,doInBackground可能还未完成,内部类AsyncTask实例持有引用,造成内存泄漏。

GC Root是什么:

是root,不会被回收

  • 运行中的线程
  • 静态对象
  • native code中的引用

所以,造成内存泄漏的原因实际上是因为“运行中的线程”是GC Root不会被回收。

  • 其它类型的线程方案,如Thread、Executor、HandlerThread、IntentService都会有这样的问题
  • AsyncTask并没有比其它线程方案更危险,需要单独强调
  • 只要时间不长,没有必要做内存泄漏的处理
  • 需要做的只是更新UI是判断一下Activity是否已被销毁