android源码解析(5)--AsyncTask源码分析以及使用中的坑

废话少说,今天来分析AsyncTask的源码,看了网上很多人都说使用AsyncTask有最大线程的约束,但是呢,自己写了一个测试代码,发现同时扔进去一万多个线程也没有问题啊,于是对自己充满了否定,是不是我用错了?然后给同事打电话一问,原来就是没有那个问题了,我去,坑了半重点内容天原来网上分析的都有问题(应该是源码更新的缘故),还是撸开袖子自己分析源码吧!

第一.怎么使用的
首先我们先看看谷歌给的例子,很简单(源码中就有)**
   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);
第二.使用很简单,看看源码吧
首先我们先分析一下都有什么属性,以及作用:
1)第一个线程池构造
    //获取cup数目
    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 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());
        }
    };
    //线程队列,应该是大家看到这里就都认为是最多128个线程了
    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);
2)第二个线程池登场了:
    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //默认线程池,也就是说我们添加的所有线程默认是添加到这里的
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    //看看这个线程池里有什么工作吧
    private static class SerialExecutor implements Executor {
        //一个类似于集合的队列(具体是什么没深究)
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        //他的执行方法,下面会调用的
        public synchronized void execute(final Runnable r) {
            //这个方法是说把一个runnable对象添加到队列的最后面
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //第一次执行肯定是空了,以后就不会执行了,而是在上面的finally中执行
            if (mActive == null) {
                scheduleNext();
            }
        }

        //操作执行下一个
        protected synchronized void scheduleNext() {
            //这个方法是说获取队列中最开始的runnable
            if ((mActive = mTasks.poll()) != null) {
                //重量级登场了,另一个线程池执行任务去了,所以真正执行任务是这个另一个线程池
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
3)两个线程池分析完了,再看看handler
    //handler,不用多说吧
    private static InternalHandler 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:
                    // 处理完的结果
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //发布进度
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        //这里就是下面结束任务是调用finish的mTask
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
        //这里有个判断,其实就再说明一件事情,就是我们设置取消,不一定会马上取消(线程就这么设计的),所以还需要标志来标识是否取消了
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
4)下面就开始我们从构造器开始看吧:
    //当前的工作
    private final WorkerRunnable<Params, Result> mWorker;
    //对任务的二次封装
    private final FutureTask<Result> mFuture;

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     * 这里说创建一个AsyncTask必须要在主线程中,对吗?分线程不可以吗?经过本人测试是可以的其实,至于为
     * 什么可以,我查阅了一下网上说的为什么必须要在主线程,他们说其实主要是因为handler必须在主线程初始
     * 化,但是我们返回去看看这里handler是怎么创建的,
     * public InternalHandler() {
     *      super(Looper.getMainLooper());
     * }
     * 我们可以看到,这里本来就是得到的主线程的Looper,也就是说不论你在哪个线程创建都会在主线程处理结果
     * 至于为什么,那就要分析handler源码了
     */
    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);
                }
            }
        };
    }
我们创建一个AsyncTask,构造器创建了一个要执行的任务,下面就开始分析execute方法:
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    //这里说明一下:虽然注解注明必须要在主线程中执行,但是经过测试在子线程也可以的,不过onPeExecute
    //方法就不是在主线程中执行了,而是在当前线程执行,onPostExecute方法仍然在主线程
    @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;
    }
好了,任务放到第二个线程池里了,然后我们可以看看上面第二个线程池干了什么工作?将任务放到队列里最后面
,然后开始从头取出任务交给第一个线程池去工作,是不是顺起来了。那么第一个线程池执行的Runnable又是什
么呢?可以看到其实就是我们构造器中创建的那个mWorker任务,然后再去构造器中看看WorkerRunnable
里面做了什么:
         mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                //设置优先级
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //开始执行这个方法,具体什么操作交由我们自己来实现
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                //执行完了还是向主线程发送结果
                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;
    }
然后又回到上面handler里面处理完成的工作了:
       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:
                    // 到这里处理完成的结果,result.mTask其实就是当前AsyncTask,看上面
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
继续:
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
        //执行结束
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
好了,至此整个执行过程全部完整分析结束了。下面开始说说都有什么坑:

第三:使用中的一些坑
1)就是面试的时候经常会问什么128个限制啊什么的,经过分析默认情况下使用是没有这个限制的,记住是默认,
  但是下面这种用法就会有:
   mMyAsy.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
其实这就是说我们自己传入了一个线程池,替代了默认的那个线程池,为什么会有限制呢?源码分析分析:
    @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;
    }
2)其实这个坑也不能说是AsyncTask中的坑,就是线程同步问题。我们做相机开发的时候由于bitmap占用大量内
  存,所以时刻需要主动去回收图片,但是处理图片过程还必须放在异步中执行,所以就会出现一个线程已经回收
  了图片了,另一个还在使用中...挂了。解决方法,个人是说两个线程各持有一把锁,如果你想要回收图片,就
  去检查对方是否释放了锁(也就是处理完了),如果处理完了,则可以回收销毁,否则我们就把我们自己所持有的
  锁释放掉,这样另一个线程执行完了检查我们的锁之后就可以回收了。
3)源码分析的时候说了,AsyncTask不是只能在主线程中使用,分线程也可以,但是onPreExecute()方法会
  在同一个线程中执行,只有onPostExecute方法会在主线程中执行,原因就是
    private static class InternalHandler extends Handler {
        public InternalHandler() {
            //它默认就去调用主线程的Looper了,所以肯定会到主线程执行最后的结果
            super(Looper.getMainLooper());
        }
        //省略...
    }
不过好像看谷歌的英文注释,确实是应该在主线程中初始化才可以,不过谷歌本来就是用来否定的...

4)就是我们调用cancle()方法不一定会立即执行取消操作,因为这是线程的一个机制,所以会造成什么结果呢,
  就是加入说我打开了一个页面,启动asynctask联网,这时候呢,我手机屏幕旋转了,页面销毁掉重新创建了
  然后再一次联网去了,按说页面销毁的时候我们会执行取消操作,但是SB不一定马上执行啊,结果就是第一次联
  网成功了刷新页面,页面早就已经重建了,肯定就Crash了。
发布了62 篇原创文章 · 获赞 28 · 访问量 12万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章