android源码解析(5)--AsyncTask源码分析以及使用中的坑
2017-03-19 22:28
696 查看
废话少说,今天来分析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了。
相关文章推荐
- Android开发——AsyncTask的使用以及源码解析
- Android开发----AsyncTask的使用以及源码解析
- [置顶] Android开发——AsyncTask的使用以及源码解析
- Android AsyncTask使用以及源码解析
- Android中从源码分析关于AsyncTask的使用
- android smack源码分析——接收消息以及如何解析消息
- Android中AsyncTask使用教程及源码分析
- AsyncTask使用以及源码分析
- android源码解析(6)--HandlerThread分析及使用对比
- Android中从源码分析关于AsyncTask的使用
- Android系统分析之AsyncTask源码解析
- Android AsyncTask使用步骤与源码分析
- android smack源码分析——接收消息以及如何解析消息
- Android中AsyncTask的使用与源码分析
- Android中AsyncTask的使用与源码分析
- android smack源码分析——接收消息以及如何解析消息
- Android中AsyncTask的使用与源码分析
- android 滑动侧边栏 SlideMenu 源码分析 以及使用手册
- Android MemoryFile 源码分析以及使用
- Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析