AsyncTask学习笔记

来源:互联网 发布:sql server c# output 编辑:程序博客网 时间:2024/06/05 07:34

1)AsyncTask 用法

public abstract class AsyncTask<Params, Progress, Result> {         .....................}
 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");      }  }

由上述代码可知,AsyncTask是抽象类,需要类去继承并且子类需要指定3个泛型,它们分别对应doInBackground(),onProgressUpdate(),onPostExecute()的参数类型。它的核心方法:onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute(),publishProgress(),
这其中只有doInBackground()是在子线程执行,其余都在主线程中执行。

2)执行的顺序:
onPreExecute()–>doInBackground()–>publishProgress()onProgressUpdate()–>onPostExecute()

  1. onPreExecute()
    这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  2. doInBackground(Params…)
    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。
  3. onProgressUpdate(Progress…)
    当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
  4. onPostExecute(Result)
    当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

3)AsyncTask 源码分析
1.从execute()开始:

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

调用了executeOnExecutor(sDefaultExecutor, params)方法。

2.executeOnExecutor(sDefaultExecutor, params)

 @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是什么?        *mFuture是什么?runnable?        */        exec.execute(mFuture);        return this;    }

由上可知,当前任务状态为RUNNING时,再次调用executeOnExecutor()会抛出IllegalStateException(“当前任务已经在执行”),当前任务状态为时FINISHED时,同样会抛出IllegalStateException异常,因为在AsyncTask中一个任务只能执行一次即使执行过程中被cancell掉 往下看调用了onPreExecute(),然后mWorker,mFuture是什么?先不管,再往后 exec.execute(mFuture),终于执行任务了。

3.exec是什么?
exec是我们传进来的sDefaultExecutor,那么sDefaultExecutor是什么?

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

到这可以得知sDefaultExecutor 其实是一个SerialExecutor 对象,调用它的execute(Runnable r)方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中ArrayDeque的poll()会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。而是在finally语句块中调用了scheduleNext()方法。SerialExecutor 作用是让线程池严格的按照任务添加的顺序执行,一个结束,一个开始。

4.mFuture,mWorker是什么?

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

mFuture 是一个FutureTask对象,实现了RunnableFuture接口,mWorker 是一个WorkerRunnable对象,实现了Callable接口。

FutureTask类:

public class FutureTask<V> implements RunnableFuture<V> {    public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable    }}

现在来看FutureTask的run()方法

public void run() {        if (state != NEW ||            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))            return;        try {        //c就是传入的mWorker             Callable<V> c = callable;            if (c != null && state == NEW) {                V result;                boolean ran;                try {                //执行mWorker的call()方法                    result = c.call();                //将ran设置为true                    ran = true;                } catch (Throwable ex) {                    result = null;                    ran = false;                    setException(ex);                }                //ran为true执行set(result);                if (ran)                    set(result);            }        } finally {            runner = null;            int s = state;            if (s >= INTERRUPTING)                handlePossibleCancellationInterrupt(s);        }    }

由上可知在FutureTask的run()方法中调用了mWorker的call()方法,现在我们来看mWorker的run()

 mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                 ........                //noinspection unchecked                Result result = doInBackground(mParams);                Binder.flushPendingCommands();                return postResult(result);            }        };

doInBackground()方法被调用,再由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;    }    private static Handler getHandler() {        synchronized (AsyncTask.class) {            if (sHandler == null) {                sHandler = new InternalHandler();            }            return sHandler;        }    }     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://   onProgressUpdate在此调用               result.mTask.onProgressUpdate(result.mData);                    break;            }        }    }@WorkerThreadprotected final void publishProgress(Progress... values) {        if (!isCancelled()) {  getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();        }    }

这段代码就是一个handler发送消息和接收消息的过程。
当msg.what==MESSAGE_POST_PROGRESS时,调用onProgressUpdate()方法,信息源是来自publishProgress()方法,正因如此,在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作
当MESSAGE_POST_RESULT时, result.mTask就是AsyncTask,调用他的finish()方法,在这里调用了 onPostExecute(result)。

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

到这mWorker 的run()执行完了,那么mFuture 的done()什么时候执行?回到FutureTask.run()方法,

   //ran为true执行set(result);    if (ran)  set(result);

mFuture 的 set(result);

    protected void set(V v) {        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {            outcome = v;            U.putOrderedInt(this, STATE, NORMAL); // final state            //注意这里            finishCompletion();        }    }   private void finishCompletion() {       ..............此处省略       //执行done()          done();       ..............此处省略    }

4)AsyncTask缺陷与解决
1.AsyncTask类包含一个全局静态的线程池THREAD_POOL_EXECUTOR,线程池的配置参数如下:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();private static final int CORE_POOL_SIZE = CPU_COUNT + 1;private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 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);    /**     * An {@link Executor} that can be used to execute tasks in parallel.     */public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

当线程池中已有线程个数达到MAXIMUM_POOL_SIZE ,并且缓冲队列已满,如果此时在向线程提交任务,将会抛出RejectedExecutionException

解决方案:替换默认的RejectedExecutionHandler使用
ThreadPoolExecutor.DiscardOldestPolicy()代替。
在调用executeOnExecutor替换线程池。
注意:根据需求合理的选择和设置线程池。

 private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,             MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
  public static class DiscardOldestPolicy implements RejectedExecutionHandler {        public DiscardOldestPolicy() { }        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {            if (!e.isShutdown()) {                e.getQueue().poll();                e.execute(r);            }        }    }}

2.AsyncTask与他所在的activity 的生命周期不是一致的,activity 终止,AsyncTask并不会终止,它会以它自有的方式继续运行,即使你退出了整个应用程序,AsyncTask提前结束的唯一方法是通过调用AsyncTask.cancel()进行取消。由于不必要的后台线程会导致app阻塞的风险,或者内存泄露。当不再需要一个AsyncTask时,一定要取消它,防止在app执行期间引起任何问题。
而且即使调用了cancel() , 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。
3.AsyncTask内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

非静态内部类或非静态匿名类会隐式的持有外部类的引用,外部类就有可能发生泄漏。

4.屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

5)AsyncTask使用注意事项
1.Task的实例必须在UI thread中创建。

/* Creates a new asynchronous task. This constructor must be *invoked on the UI thread. */    public AsyncTask() {         ........省略}

2.. execute方法必须在UI thread中调用 。( @MainThread修饰的方法必须在主线程调用)

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

3.不要手动的去调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法 .
4. 一个AsyncTask对象只能执行一次,否则会有异常抛出”java.lang.IllegalStateException: Cannot execute task: the task is already running”。
5. doInBackground()方法并不是在onPreExecute()方法做完全部操作才开始执行。

参考:http://blog.csdn.net/hitlion2008/article/details/7983449