美文网首页
走马观花之-AsyncTask

走马观花之-AsyncTask

作者: OkCoco | 来源:发表于2018-03-27 21:14 被阅读0次

AsyncTask类声明

public abstract class AsyncTask<Params, Progress, Result>

  AsyncTask可以更简单地使用UI线程。该类允许你在后台执行操作,且不需要操纵线程和Handler就可以将结果抛到UI线程进行处理。
  AsyckTask被设计来作为线程和Handler之间的帮助类,但是并没有构成一个通用的线程框架。AsyckTasks应当用于短时操作(最多几秒钟).如果你需要保持线程存活一段时间,建议使用java.util.concurrent包中的API,例如Executor或ThreadPoolExecutor、FutureTask
  一个异步任务被一个运行在后台线程,但是将结果抛出到UI线程的Computation所定义。
一个异步任务被3个泛型和4个步骤所定义:
3个泛型:

  • Params
  • Progress
  • Result

4个步骤:

  • onPreExecute()
  • doInBackground()
  • onProgressUpdate()
  • onPostExecute()

使用

  AsyckTask必须被其子类来使用,子类至少要实现一个方法doInBackground(),通常也会重写第二个onPostExecute().

下面是一个例子:

  private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
      protected Long doInBackground(URL... urls) {
          int count = urls.length;
          long totalSize = 0;
          for (int i = 0; i < count; i++) {
              totalSize += Downloader.downloadFile(urls[i]);
              publishProgress((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
      protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
          showDialog("Downloaded " + result + " bytes");
      }
  }

  一旦一个任务被创建,通过调用:

    new DownloadFilesTask().execute(url1, url2, url3);

  就可以执行了。

3个泛型:

  • Params:发送给任务执行的参数类型
  • Progress:任务在后台执行的进度单位(Integer...)
  • Result:后台任务执行的结果

  如果不需要泛型,使用Void来代替。例如,不想抛出任务进度,可用Void替换Integer等

4个不步骤:

  当一个异步任务在执行时,这个任务要经过4个步骤:

  • onPreExecute():在任务开始执行之前触发,处于UI线程。这个步骤通常用来构建任务,显示任务进度条等工作
  • doInBackground(Params... params):在onPreExecute()执行完毕之后立即调用,运行在后台线程。这个步骤通常用来执行耗时操作。异步任务的参数将传递到这个步骤中进行处理。执行结果被返回到最后一个步骤。也可以
    使用publishProgress()来发布任务的进展。这些值会被发送到UI线程,在onProgressUpdate()中进行回调处理
  • onProgressUpdate():调用publishProgress()之后会被触发,执行在UI线程。执行时间是不确定的。该方法被使用来显示任务进度。
  • onPostExecute():后台任务结束之后触发,执行在UI线程。后台任务的结果会以一个参数的形式传递到这个步骤中

取消任务:

  一个任务能在任何时候通过调用cancel()进行取消。在调用之前可调用isCancelled()方法进行判断再决定是否调用。
  调用了该方法之后,onCancelled()方法将会替代onPostExecute()方法在DoInBackground()返回时被调动。为了保证该任务能被尽可能快的取消掉,应该周期性地在doInBackground()调用isCancelled()进行检查。

线程的规则:

要使这个类正常工作,必须遵循一些线程规则;
1、AsyncTask必须加载在UI线程
2、AsyncTask实例必须在UI线程创建
3、execute()方法必须在主线程执行
4、onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()等方法不要手动调用
5、同一任务执行两次会报异常

另外插一句关于类的构造方法和代码块

     new一个对象时,执行父类的静态变量和静态代码块-->自身的静态代码块-->父类的代码块-->父类的构造方法-->自身的代码块-->自身的构造方法

源码追踪:

  • 四个步骤的先后执行顺序
    new DownloadTask().execute(url);

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

    sDefaultExecutor其实就是一个SerialExecutor:
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    SerialExecutor的定义如下:
    private static class SerialExecutor implements Executor {
        //ArrayDeque其实就是一个双向队列
        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);
            }
        }
    }

  可以看到并猜测,通过sDefaultExecutor调用execute()方法是,就是将任务包装成一个Runnable对象,然后将该任务添加进ArrayDeque任务队列,然后在通过调用scheduleNext()执行下一个。THREAD_POOL_EXECUTOR是一个线程池,允许核心线程超时。可以看到,SerialExecutor保证了任务是一个接一个执行的,只有上一个执行了,下一个才会跟着执行,保证了高并发情况下的正常执行。

继续追踪executeOnExecutor()方法:

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

  确实,一个任务执行的时候,先调用 onPreExecute()做预备工作,接着讲mFuture当作参数传进sDefaultExecutor中,在其中会调用
scheduleNext()执行任务。就会执行 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;
            }
        };

可以看到,在其中调用了doInBackground(mParams)方法,故而该方法第二顺序被调用了。接着会调用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;
    }

可以看到,其实就是将结果包装成AsycnTaskResult通过Handler进行处理,Handler识别该操作的标识是MESSAGE_POST_RESULT:

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

其实就是调用finish方法:

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

  可以看到,任务取消了就会回调 protected void onCancelled()方法,否则回调 onPostExecute(result)方法,接着在改变状态值。
所以回调顺序目前是:onPreExecute()-->doInBackground()-->onPostExecute().
那onProgressUpdate()是在什么时候调用的呢?官方推荐我们看publishProgress()方法:

    @WorkerThread(此时还处在子线程,方法体中通过Handler转换到主线程)
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

    //就会执行到这句代码:
    result.mTask.onProgressUpdate(result.mData);

另外,AsycnTaskResult的数据结构:

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

  可以看到,对成员变量不存在get和set方法,故而mTask和mData其实就是通过构造方法赋值的。见publishProgress().
Data是一个泛型,可以是任何类型。

相关文章

网友评论

      本文标题:走马观花之-AsyncTask

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