AsyncTask使用注意

1、一个AsyncTask对象只能执行一次任务,否则会报错。
 private void test() {
        MAsyncTask asyncTask = new MAsyncTask();
        for (int i = 0; i < 3; i++) {
            asyncTask.execute("456" + i);
        }
    }

       class MAsyncTask extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... strings) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Loger.e("doInBackground--->" + strings[0]);
            return "result";
        }
    }

这里执行会报错,报错原因是因为在executeOnExecutor方法中会检查AsyncTask的状态,一旦AsyncTask的Status状态不是PENDING就不可以执行executeOnExecutor方法。所以会报错,报错是要不正在执行,要不就是已经执行过了,一个任务只能执行一次。

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;
    }
2、AsyncTask.execute()和AsyncTask.executeOnExecutor()的区别

1)先看AsyncTask.execute()方法,默认使用了sDefaultExecutor 参数

        for (int i = 0; i < 5; i++) {
             new MAsyncTask().execute("execute--->" + i);
        }
执行的结果是:
04-22 14:01:32.345 5189-5230/E/Loger: doInBackground--->execute--->0
04-22 14:01:35.345 5189-5230/ E/Loger: doInBackground--->execute--->1
04-22 14:01:38.346 5189-5230/ E/Loger: doInBackground--->execute--->2
04-22 14:01:41.348 5189-5230/E/Loger: doInBackground--->execute--->3
04-22 14:01:44.350 5189-5230/E/Loger: doInBackground--->execute--->4

从上面的结果能够看出execute执行任务都是串联的,不能并行处理任务。为什么新建了5个AsyncTask对象,执行任务是串行的,执行时间是14:01:32依次增加,主要是因为AsyncTask中定义的成员变量sDefaultExecutor 是静态的,所有的AsyncTask对象都是共享的

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

所以当所有的任务执行的时候会共享一个SerialExecutor对象,在SerialExecutor中使用的双端队列,只能排序执行了。
2)AsyncTask.executeOnExecutor()执行实例

for (int i = 0; i < 5; i++) {
      new MAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
              "executeOnExecutor--->" + i);
 }
04-22 14:16:48.851 5353-5396/E/Loger: doInBackground--->executeOnExecutor--->1
04-22 14:16:48.851 5353-5399/E/Loger: doInBackground--->executeOnExecutor--->4
04-22 14:16:48.851 5353-5395/E/Loger: doInBackground--->executeOnExecutor--->0
04-22 14:16:48.852 5353-5398/E/Loger: doInBackground--->executeOnExecutor--->3
04-22 14:16:48.852 5353-5397/E/Loger: doInBackground--->executeOnExecutor--->2

这里直接使用了AsyncTask的内部的线程池对象,也可以自己使用,但是输出的结果能够看出,执行任务的时间是并行的,都是在14:16:48执行的,这里就是直接调用了线程处理。

所以当需要串行执行任务的时候使用AsyncTask.execute(),当需要并行执行多个任务的时候使用AsyncTask.executeOnExecutor()。

3、AsyncTask带来的内存泄漏

1)内部类AsyncTask静态化
为什么当内部定义的AsyncTask需要静态化,其实这与AsyncTask本身没有多大的关系,先谈一下内部类静态化和不静态的区别
非静态内部类:当MAsyncTask定义在Activity的内部的时候,MAsyncTask生存是一直依赖Activity的,可以理解为Activity的内部成员一样,当外部调用finish()结束Activity时,由于MAsyncTask依赖Activiy,持有Activity的引用,造成Activity不能回收。
静态内部类:当MAsyncTask定义为static的时候,内部静态类就等同于外部定义的单独的一个类,与Activity本身已经脱离了关系。这样就不会造成MAsyncTask依赖于Activity。

2)弱引用的使用
当AsyncTask中需要Activity的对象的时候,如果AsyncTask持有Activity对象,当Activity退出了,而此时AsyncTask才刚刚进行到了一半,这时候线程还得继续执行,但是Activity对象一直不能及时的回收,本应该在Activity结束了就应该被回收的。这就引发了内存的泄漏。

   private void test() {
        new MAsyncTask(new WeakReference<Activity>(TestActivity.this)).execute("execute--->" + 0);
    }

    class MAsyncTask extends AsyncTask<String, Void, String> {

        private  Activity mActivity;

        public MAsyncTask(WeakReference<Activity> weakReference) {
            this.mActivity = weakReference.get();
        }
    }

对于WeakReference包装的对象,如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收,这样就避免了内存你的泄漏。

所以使用AsyncTask时,使用内部类就使用static进行修改,要不就单独写在外面,对于Activity作为参数时,传递弱引用参数作为传递的对象,这样就避免了内存的泄漏。

总结:
1、每一个AsyncTask对象只能执行一个任务
2、AsyncTask.execute()是串行执行多任务(按序执行),AsyncTask.executeOnExecutor()可以多任务同步执行
3、AsyncTask使用时定义为静态的,持有Activity引用采用弱引用进行包装,再提供给AsyncTask使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。