AsyncTask的用法及注意点

来源:互联网 发布:南阳网络推广哪家好 编辑:程序博客网 时间:2024/06/05 01:09

AsyncTask是什么

AsyncTask是用来让我们在UI线程中调用以进行一些异步任务的类,通常要求这个异步任务的持续时间很短,最多持续几秒钟,在完成后台任务时,会将结果通知到主线程进行处理。
说的更本质一些,AsyncTask=Thread+HandlerThread用来进行后台任务,Handler用于处理UI线程的消息,例如任务完成后更新视图操作等。
AsyncTask类从apiLevel=3开始才有。

AsyncTask基本用法

Step 1
实现一个自己的类继承自:class AsyncTask<Params, Progress, Result>
三个泛型分别表示:

  1. Params:表示执行任务的参数,可以传入多个
  2. Progress:包含耗时操作的进度信息,用于界面的显示更新
  3. Result:耗时任务的执行结果

对于不需要的内容可以使用Void类型。

Step 2
实现其中的抽象方法:protected abstract Result doInBackground(Params... params)方法,该方法中就是我们需要进行的耗时任务,如网络查询操作等,然后将操作的结果进行返回。
该方法可以理解为Thread类中的run方法。

Step 3
Step 2执行完成后,AsyncTask将会调用protected void onPostExecute(Result result)方法,参数就是我们返回的Result,该方法在UI线程中执行,用于更新界面显示,不强制要求实现。

Step 4
实例化我们自定义的任务类,并执行。

MyAsyncTask task = new MyAsyncTask();task.execute(param1, param2, ...);

通过以上代码可以实现我们的需求,但是有一点不好的是,调用execute方法在不同的Android版本上的表现不太一样:

switch(apiLevel)    apiLevel = 3 : 单线程执行    apiLevel > 3 and apiLevel < 11 : 多线程执行    apiLevel >= 11 : 单线程执行

所以应该尽量避免这种有歧义的代码,在执行时我们需要指定任务的执行方式,单线程和多线程的执行代码示例如下(apiLevel>=11):

task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, param1, param2, ...);task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, param1, param2, ...);

这里的单线程表示在当前APP进程中,所有的异步任务都将串行执行,即执行完一个再执行后一个,而多线程就是指我们可以同时运行多个异步任务的后台操作。大家都知道多线程的好处就是可以提高运行效率,但是却也带来了同步的问题,因此这两种方法都有存在的必要。
变长参数列表param1, param2, ...在调用时没有需求可以不写,其本质就是一个数组。

PS
完整的一次异步任务中方法的执行顺序是:
1. onPreExecute:UI线程
2. doInBackground:子线程
3. onPostExecute:UI线程

还有一个关于进度显示的方法protected void onProgressUpdate(Progress... values),该方法运行在UI线程,可以在任意时机被调用,而何时被调用则是取决于我们调用protected final void publishProgress(Progress... values)的时机。

我们可以在任意时间,任意线程中publishProgress,不过在主线程中调用似乎没有什么必要,onPreExecute表示任务还没开始,此时进度为0,onPostExecute此时任务已经完成,进度为已满的状态。

AsyncTask用对了吗~?

1. 任务只给执行一次~

我们的每个任务示例只可以被execute一次,如果多次调用被抛出IllegalStateException异常,因此任务执行完成后请及时赋值null

AsyncTask共有3种状态:

  1. PENDING 每个实例构造完成的初始值
  2. RUNNING 表示使用者调用了executeOnExecutor或execute,此时会立刻调用onPreExecute方法,但是不会立即执行doInBackground方法
  3. FINISHED 表示onPostExecute方法执行完成

在任务执行时需要判断当前Task是否是PENDING状态,不是的话将抛出异常,每个Task类只可以被执行一次。

2. 不要在子线程中操作主线程中的数据

在子线程中(doInBackground方法)更新视图应该没人会做,但是却会有很多情况下在子线程中更新视图中的数据。
比较常见的情况是修改ListView中的数据,例如下面的这种代码:

// doInBackground方法中// TODO 获取数据操作...// mDataList表示ListView加载用到的数据mDataList.removeAll();mDataList.addAll(新获取的数据);

写出这样的代码可能会造成在滑动列表时产生异常:IndexOutOfBoundsException,而且即使没有抛异常也不代表加载显示的数据正确的。在这种情况下的推荐做法是,将后台进行的耗时操作的结果数据返回(泛型Result,在本例中可以定义为ArrayList<String>),并在onPostExecute方法中对该数据进行展示,保证所有和UI更新的代码都在UI线程中执行。

3. 适当并及时地终止任务

AsyncTask主要用途就是异步获取数据,并用于页面加载,所以当页面不存在时,就应该适当地终止任务以节约资源。

例如在一个Activity调用onStart时,我们需要从服务端获取最新数据用于界面显示,此时我们需要execute一个AsyncTask,在onPostExecute方法中我们会写一些关于页面更新的逻辑。
但是当Activity调用了onStop方法,此时页面已经变得不可见,我们也并不希望有网络数据返回时再去更新界面显示,因此在此时我们就需要调用AsyncTaskcancel方法去取消任务。

如果当前任务还处于运行RUNNING状态,调用cancel方法后将不会执行onPostExecute方法,并且在doInBackground方法中也可以通过函数isCancelled来随时获取当前任务的状态,如果返回true,我们可以不进行接下来的操作(例如执行多个网络请求,在每个请求执行之前判断来决定是否要继续执行)。

举个栗子

通过一个简单的例子来了解一下AsyncTask类的具体使用方法:

public class TaskActivity extends Activity {    AsyncTask<Void, Integer, ArrayList<String>> mTask;    @Override    protected void onStart() {        super.onStart();        startTask();    }    @Override    protected void onStop() {        super.onStop();        cancelTask();    }    // 开始任务    private void startTask() {        if (mTask != null) {            mTask.cancel(true);        }        mTask = new MyAsyncTask();        mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);    }    // 取消任务    private void cancelTask() {        if (mTask != null) {            mTask.cancel(true);            mTask = null;        }    }    /**     * 自定义异步任务类     */    private class MyAsyncTask extends AsyncTask<Void, Integer, ArrayList<String>> {        @Override        protected void onPreExecute() {            // TODO 在这里可以加载LoadingView提示用户        }        @Override        protected ArrayList<String> doInBackground(Void... voids) {            // TODO 在这里执行耗时的操作,如网络请求,大量的计算等等            ArrayList<String> result = new ArrayList<String>();            for (int i = 0; i < 100; i++) {                try {                    Thread.sleep(100);                    publishProgress(i);                    result.add("" + i);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return result;        }        @Override        protected void onPostExecute(ArrayList<String> result) {            mTask = null;            // TODO 在这里进行相应的界面更新逻辑            // 例如将result加载到ListView中进行显示        }        @Override        protected void onProgressUpdate(Integer... progress) {            // TODO 将进度更新到界面,本例用用整数表示百分比        }    }}

以上的代码实现了一个自定义的异步任务类MyAsyncTask,该类的主要功能是每隔100ms更新一下进度,最后将所有的进度值加到链表中作为耗时操作的执行结果返回,用于界面显示,是不是很简单呢~!