Android入门教程 | AsyncTask 使用介绍

2,943 阅读5分钟

AsyncTask 有助于使用 UI 线程。这个类能让你不主动使用多线程或 Handler,在子线程种执行耗时任务,并在UI线程发布结果。

AsyncTask 是一个在不需要开发者直接操作多线程和 Handler 的情况下的帮助类,适用于短时间的操作(最多几秒)。 如需长时间的线程操作,建议使用多线程包 java.util.concurrent 中的功能,比如线程池。

假设我们有个需要在后台线程中运行的异步计算任务,并且结果需要更新 ui。 那我们需要关注3个范型参数:Params, Progress 和 Result。 再关注4个步骤方法: onPreExecutedoInBackgroundonProgressUpdate 和 onPostExecute

属性介绍

使用 AsyncTask 之前,我们先看一下它的三个类型。

AsyncTask<Params, Progress, Result>
属性描述
Params 执行任务前,传入的参数的类型
Progress 后台线程执行的时候,用来表示进度的类型
Result 表示执行结果的类型

这 3 个类型需要开发者自己指定。比如指定 String, Integer 等。这 3 个类型在后面的方法里会用到。

不用的泛型可以用Void表示。例如

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

方法介绍

要使用 AsyncTask ,必须新建一个类来继承它,并且重写 doInBackground 方法。通常也会重写 onPostExecute 方法。 执行异步任务的时候,我们主要关心下面这4个方法。

方法描述
onPreExecute() 执行任务前在ui线程调用。通用用来设置任务,比如在界面上显示一个进度条。
Result doInBackground(Params... params) onPreExecute() 结束后立即调用这个方法。耗时的异步任务就在这里操作。执行任务时传入的参数会被传到这里。异步任务的中间结果在这里可以用 publishProgress 发送到主线程。
onProgressUpdate(Progress... values) 在 ui 线程中执行。后台任务还在进行的时候,这里负责处理进度信息。比如在这显示进度条动画,修改文字显示等。
onPostExecute(Result result) 后台任务结束了调这个方法。它在 ui 线程执行。最后的结果会传到这。

AsyncTask 的三种状态

每个状态在一个任务的生命周期中只会被执行一次。

状态描述
PENDING等待(还没有开始执行任务)
RUNNING执行中
FINSHED完成

用法示例

虚构一个计算任务

/**
 * 虚拟的计算任务
 */
private class CalculationTask extends AsyncTask<Float, Integer, Float> {
    protected Float doInBackground(Float... inputs) {
        Log.d(TAG, "doInBackground thread ID = " + Thread.currentThread().getId());
        long step = 0;
        float result = 0;
        for (float f : inputs) {
            // 假设这里有一些耗时的操作
            result += f;
        }
        while (step < 5) {
            result += step;
            step++;
            publishProgress((int) step);
        }
        return result;
    }

    protected void onProgressUpdate(Integer... progress) {
        Log.d(TAG, "onProgressUpdate thread ID = " + Thread.currentThread().getId());
        Log.d(TAG, "onProgressUpdate: " + progress[0]);
    }

    protected void onPostExecute(Float result) {
        Log.d(TAG, "onPostExecute thread ID = " + Thread.currentThread().getId());
        Log.d(TAG, "任务执行完毕");
    }
}

// 执行任务
new CalculationTask().execute(1.2f, 2.3f, 6.3f);

取消任务

调用 cancel(boolean) 可随时取消任务。取消任务后 isCancelled() 会返回true。

调用这个方法后,后台任务 doInBackground(Object[]) 执行完毕后会调用 onCancelled(Object) 而不再是 onPostExecute(Object)。 为保证任务能被及时地取消,在 doInBackground(Object[]) 中应该经常检查 isCancelled() 返回值

线程规则 Threading rules

  • 异步任务必须从UI线程启动
  • 必须在UI线程实例化AsyncTask类
  • 必须在UI线程调用execute(Params...)
  • 不要手动调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)
  • 同一个异步任务实例只能被执行一次。重复执行同一个异步任务实例会抛出异常(IllegalStateException)。

AsyncTask 相关面试题

1. AsyncTask 是什么?能解决什么问题

2. 谈谈 AsyncTask 的三个泛型参数作⽤

3. 说说AsyncTask的原理

构造⽅法中创建了⼀个 WorkRunnable 和⼀个 FutureTask 对象,

在 WorkRunnable的Call ⽅法中调⽤ doInBackground ⽅法,并获取 Result 返回值,然后返回调⽤postResult ⽅法的返回值,创建 FutureTask 时传⼊了 WorkRunnable 对象。

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            return postResult(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);
            }
        }
    };
}

postResult ⽅法

getHandler⽅法获取⾃带Handler对象,来获取Message

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

这个内部⾃带InternalHandler构造⽅法中传⼊getMainLooper()返回值,即主线程Looper,

然后在handlerMessage⾥⾯针对msg.what分别执⾏了finish和onProgressUpdate⽅法,其中finish⽅法则是调⽤onCancelled或onPostExecute⽅法

private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
     public InternalHandler() {
          super(Looper.getMainLooper());
     }
     @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;
               }
          }
}
private void finish(Result result) {
     if (isCancelled()) {
          onCancelled(result);
     } else {
          onPostExecute(result);
     }
     mStatus = Status.FINISHED; 
}

看看 AsyncTask 的 execute ⽅法,返回了 executeOnExecutor 的⽅法,并传⼊sDefaultExecutor 和 params 为参数,params 即 AsyncTask 的 doInBackground中传⼊的 params ,

⽽ sDefaultExecutor 是默认的串⾏执⾏器(线程池),⼀个SerialExecutor 再看看executeOnExecutor ⽅法,先判断了任务状态,如果是 RUNNING 或FINISHED,则会抛出异常,

然后会把当前状态从 PENDING 改为 RUNNING,把⼊参 params 传到WorkRunnable 对象中,再调⽤传⼊的 sDefaultExecutor 的 execute ⽅法,传⼊mFuture 为参数

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

execute ⽅法中调⽤ mTasks 的 offer ⽅法添加⼀个任务到缓存队列中,在 run ⽅法中就调⽤了前⾯传⼊的 mFuture 的 run ⽅法,

然后执⾏ THREAD_POOL_EXECUTOR 的 execute ⽅法,任务的实际执⾏就在这,THREAD_POOL_EXECUTOR 就是⼀个线程池

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new 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);
          }
      }
}

4. 你觉得AsyncTask有不⾜之处吗?

AsyncTask 使⽤起来⽐较轻量,但是⾃身也存在⼀些问题。

主要表现在:

  • cancel ⽅法实现不是很好。如果你调⽤了 AsyncTask 的 cancel(false) ⽅法, doInBackground() 仍然会执⾏到⽅法结束,只是不会去调⽤ onPostExecute() ⽅法,但是实际上也是让程序执⾏了没有意义的操作。如果调⽤cancel(true),mayInterruptIfRunning 设置为 true,会使任务尽早结束,但是如果 doInBackground() 有不可被打断的⽅法,就会失效,⽐如 BitmapFactory.decodeStream() 操作。

  • 内存泄露,在 Activity 中使⽤⾮静态匿名内部类 AsyncTask 类,由于 Java 内部类的特点,内部类持有外部类引⽤,⽽由于 AsyncTask ⽣命周期可能⽐ Activity 的,当 Activity 销毁时,AsyncTask 还在执⾏,由于AsyncTask 持有 Activity 的引⽤,导致 Activity 对象⽆法回收,进⽽产⽣内存泄露。

  • 结果丢失,当屏幕旋转等造成 Activity 新创建时 AsyncTask 数据丢失的问题。当 Actviity 销毁并创建新的收,还在运⾏的 AsyncTask 会持有⼀个 Activity 的⾮法引⽤,即之前 Activity 的实例,导致onPostExecute() ⽅法⽆效。

  • 串⾏,并⾏多版本不⼀致.1.6之前为串⾏,1.6-2.3为并⾏,3.0之后⼜改为串⾏,但是可以通过executeOnExecutor() 实现并⾏处理

Android零基础入门教程视频参考