当前位置: 首页 > 工具软件 > AsynTask > 使用案例 >

AsynTask的用法和内部原理详解

蒋原
2023-12-01

简介

AsynTask方便在后台线程中执行操作,然后将结果发给主线程,从而在主线程更新UI,无需关心Thread和Handler;AsynTask只能执行短暂的后台任务,如果需要执行长时间耗时任务,推荐使用java.util.concurrent包中的ThreadPoolExecutor;使用AsynTask至少要重写doInBackground方法,和经常用来更新UI的onPostExecute方法;AsynTask的内部实现原理是线程池和Handler。

用法

MyAsyncTask myAsyncTask = new MyAsyncTask(tv_update, pro);
myAsyncTask.execute("开始执行");

static class MyAsyncTask extends AsyncTask<String, Integer, String> {
  private TextView update;
  private ProgressBar pro;
  private final String tag;

  public MyAsyncTask(TextView update, ProgressBar pro) {
    tag = MyAsyncTask.class.getSimpleName();
    this.update = update;
    this.pro = pro;
  }

  @Override
  protected void onPreExecute() {
    update.setText("开始准备执行异步任务");
  }

  @Override
  protected String doInBackground(String... params) {
    Log.e(tag, "doInBackground:" + params[0]);
    int count = 0;
    while (count < 100) {
      try {
        Thread.sleep(2000);
        count += 10;
        publishProgress(count);
      } catch (InterruptedException e) {
        e.getStackTrace();
      }
    }
    return "异步执行结束";
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
    pro.setProgress(values[0]);
  }

  @Override
  protected void onPostExecute(String s) {
    update.setText(s);
  }
}

AsynTask定义了三种泛型类(分别为Params,Progress和Result)和四步骤(onPreExecute,doInBackground,onProcessUpdate和onPostExecute);

三种泛型

Params:启动任务执行的参数,例如请求的url;

Progress:后台任务执行的百分比;

Result:后台执行任务的结果;比如网络请求json等

三种泛型不一定都需要使用,不需要使用的,可以用Void替代;

四步骤:AsynTask执行之后,先后经历四步骤分别为:

1)onPreExecute:在任务开始执行之前,在UIThread调用此方法,可以用来初始化ProgressBar等控件;

2)doInBackground:在onPreExecute执行完成之后,立刻调用后台线程做好事任务,在执行过程中可以调用publicProgress来更新任务的进度;

3)onProcessUpdate:在doInBackground方法中调用publicProgress后,立刻在UIThread中调用,可以更新UI,

4)onPostExecute:在doInBackground方法中后台耗时任务执行结束之后,立刻在UIThread中调用;

Task的执行过程

AsynTask的状态

1)PENDING:未执行

2)RUNNING:执行中

3) FINISHED:已经执行

以Android-23的源码分析;

1)调用execute(),内部调用executeOnExecutor传入sDefaultExecutor线程池

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

executeOnExecutor方法中
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
 //判断在那种状态,一个AsynTask实例只能被执行一次
 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();
  //execute中的参数传递给mWorker.mParams ;
  mWorker.mParams = params;
  //把mFuture任务交给线程池sDefaultExecutor  
  exec.execute(mFuture);
  return this;
}

executeOnExecutor方法中先判断AsyncTask实例的状态,当已经执行过之后,再次执行就会抛出异常;只有在PENDING(未执行)状态下,才能够执行,并且把状态更改为RUNNING(运行中)状态,然后执行onPreExecute()方法,可以在此方法中,执行准备工作;把execute(Params... params)方法中的参数传递给mWorker.mParams,sDefaultExecutor线程池执行mFuture任务;

sDefaultExecutor线程池就是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() {//向队列中插入一个runnable
      public void run() {
        try {
         r.run();
        } finally {
          scheduleNext();
        }
      }
    });
   //判断是否有runnable在执行,没有就调用scheduleNext方法
    if (mActive == null) {
      scheduleNext();
    }
  }

  protected synchronized void scheduleNext() {
    //从任务队列mTasks中取出任务并放到THREAD_POOL_EXECUTOR线程池中执行. 由此也可见任务是串行进行的。
    if ((mActive = mTasks.poll()) != null) {
      THREAD_POOL_EXECUTOR.execute(mActive);
    }
  }
}

SerialExecutor并不是真正的线程执行者,它只是是保证传递进来的任务Runnable(实例是一个FutureTask)串行执行,确保同时只有一个线程在执行,而真正执行任务的是THREAD_POOL_EXECUTOR线程池,当然该逻辑也体现AsyncTask内部的任务是默认串行进行的,真正执行任务的是THREAD_POOL_EXECUTOR;

THREAD_POOL_EXECUTOR线程池的配置

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU的核数
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;//核心线程数
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//最大线程数
private static final int KEEP_ALIVE = 1;//保活时间
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
  public Thread newThread(Runnable r) {
    return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128); //消息队列最大容量

public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

mWorker和mFuture是什么呢?

mWorker和mFuture都是在AsyncTask的构造函数中初始化

mWorker是WorkerRunnable的实例,在AsyncTask的构造方法中初始化

WorkerRunnable类

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}

从源码可以看出来,WorkerRunnable是Callable的实现类,内部有一个泛型数组mParams;不了解Callable可阅读线程和线程池的用法先了解下

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

WorkerRunnable内部的call()方法中调用doInBackground(mParams);并且返回返回值,call方法return返回值,调用postResult(result);

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

postResult()方法直接将参数return回去,但是在内部发送Message,InternalHandler处理message消息在InternalHandler的handleMessage中调用finish(Result result)方法,

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

如果没有取消,执行onPostExecute()方法,并且改变AsynTask实例的状态为FINISHED(已完成 ),在onPostExecute()中更新UI,

如果在doInBackground()方法中调用publishProgress()

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

如果Task没有被取消,将发送Message消息,在InternalHandler的handleMessage中调用onProgressUpdate方法,

@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}

在onProgressUpdate()中更新进度,

mFuture是FutureTask的实例;再回到executeOnExecutor中,把mFuture任务交给线程池sDefaultExecutor调用exec.execute(mFuture);当WorkerRunnable的call()方法耗时任务执行完成之后,FutureTask 调用done()方法,表示FutureTask执行完成

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

由于WorkerRunnable的call()有可能返回结果,有可能返回异常,如果返回异常,postResult(result)将不会执行,后续的onPostExecute()也无法执行,就需要在在done()中做异常处理,调用postResultIfNotInvoked();

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

会看WorkerRunnable的call()方法,先将mTaskInvoked.set(true);如果发生异常,无法设置为true,将为false,调用postResult(result);发送Message消息;在InternalHandler的handleMessage中调用finish(Result result)方法,如果Task没有被取消,执行onPostExecute()方法

以上就是AsyncTask的调用执行逻辑;

AsynTask使用注意事项

1)AsynTask的实例必须在UIThread中调用;

2)execute()必须在UIThread中调用;

3)不能手动调用onPreExecute,doInBackground,onProcessUpdate,onPostExecute方法;

4)一个AsynTask实例只能被执行一次,否则多次调用会抛出异常;

AsynTask最早是执行在一个单一线程中;在Android1.6之后,AsynTask执行在线程池中,允许多个线程同时并发执行;在Android3.1之后,又执行在一个单一线程中,避免并行操作造成常见的应用程序错误;如果需要执行并行操作,调用executeOnExecutor()方法;

以上是AsncTask的基本用法和执行流程的内部源码分析;如有问题,请多指教;

源码

 类似资料: