diff --git a/README.md b/README.md index 7e41097..66f27ea 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ Android Notes 11. [热修复](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E7%83%AD%E4%BF%AE%E5%A4%8D.md) 12. [事件分发机制](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E4%BA%8B%E4%BB%B6%E5%88%86%E5%8F%91%E6%9C%BA%E5%88%B6.md) 13. [Handler 消息循环机制](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/Handler%20%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.md) +14. [AsyncTask 源码分析](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/AsyncTask.md) +15. [HandlerThread 源码分析]() +16. [IntentService 源码分析](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/IntentService.md) +17. [View 工作原理](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/View%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.md) ##### 性能优化 @@ -62,7 +66,7 @@ Android Notes [电量优化](https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/%E7%94%B5%E9%87%8F%E4%BC%98%E5%8C%96.md) -#### Java +#### Java 基础 [final 你需要知道的一切](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/final.md) @@ -76,10 +80,20 @@ Android Notes [异常 你需要知道的一切](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%BC%82%E5%B8%B8.md) +[BIO、NIO、AIO](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/BIO%E3%80%81NIO%E3%80%81AIO.md) + [UncaughtExceptionHandler](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/UncaughtExceptionHandler.md) [Java 5 - Java 10 新特性总结](https://github.com/Omooo/Android-Notes/tree/master/blogs/Java/Java%205%20-%20Java%2010%20%E6%96%B0%E7%89%B9%E6%80%A7) +#### Java 并发 + +[线程、线程池](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%B9%B6%E5%8F%91/%E7%BA%BF%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E6%B1%A0.md) + +[线程生命周期](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%B9%B6%E5%8F%91/Java%20%E7%BA%BF%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.md) + +[并发编程的万能钥匙 - 管程 ( Monitor )](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%B9%B6%E5%8F%91/%E7%AE%A1%E7%A8%8B(Monitor).md) + #### JVM [一篇文章学完 JVM 重点知识](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/JVM.md) diff --git a/blogs/Android/AsyncTask.md b/blogs/Android/AsyncTask.md index 0484e16..5967b38 100644 --- a/blogs/Android/AsyncTask.md +++ b/blogs/Android/AsyncTask.md @@ -4,22 +4,225 @@ AsyncTask #### 目录 -1. 思维导图 -2. 概述 -3. 具体使用 -4. 源码分析 -5. 参考 +1. 前言 +2. 源码分析 -#### 思维导图 +#### 前言 -#### 概述 +AsyncTask 是一个轻量级的异步任务类、抽象泛型类。现在基本上没人会用到它了,但是学习一下源码还是很有必要的。很早之前就阅读 AsyncTask 源码了,当时看的迷迷糊糊的,很大一部分原因是我对线程池以及 Future、Callable 不熟悉,所以在看源码之前,一定要熟悉这一块的知识,不熟悉的可以先看看 [线程、线程池](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%B9%B6%E5%8F%91/%E7%BA%BF%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E6%B1%A0.md)。 -AsyncTask 是一个轻量级的异步任务类、抽象泛型类。 - -#### 具体使用 +在熟悉了 Callable、FutureTask、ArrayDeque 之后,在看 AsyncTask,简直不要太简单。 #### 源码分析 -#### 参考 +首先看一下 AsyncTask 的成员变量: + +```java + //串行的任务执行器 + public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); + + private static final int MESSAGE_POST_RESULT = 0x1; + private static final int MESSAGE_POST_PROGRESS = 0x2; + + private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; + private static InternalHandler sHandler; + + //任务队列,是 Callable 类型 + private final WorkerRunnable mWorker; + private final FutureTask mFuture; +``` + +那 SerialExecutor 到底做了什么呢? + +```java + private static class SerialExecutor implements Executor { + final ArrayDeque mTasks = new ArrayDeque(); + 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); + } + } + } +``` + +所以这里就明白了,默认任务是串行执行的,越早放入的任务越早执行,同时当这个任务执行完毕之后,就会再从头部去取任务,也就达到了循环执行任务的目的了。 + +然后就是 AsyncTask 的构造方法: + +```java + public AsyncTask() { + this((Looper) null); + } + public AsyncTask(@Nullable Looper callbackLooper) { + //获取主线程 Handler + //用于在任务执行完和任务执行过程回调函数更新进度 + mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() + ? getMainHandler() + : new Handler(callbackLooper); + + mWorker = new WorkerRunnable() { + public Result call() throws Exception { + mTaskInvoked.set(true); + Result result = null; + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + //重写的后台操作方法 + result = doInBackground(mParams); + Binder.flushPendingCommands(); + } catch (Throwable tr) { + mCancelled.set(true); + throw tr; + } finally { + postResult(result); + } + return result; + } + }; + + mFuture = new FutureTask(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() 方法: + +```java + //调用这个方法,默认是 sDefaultExecutor,也就是上面的 SerialExecutor,即串行执行 + public final AsyncTask execute(Params... params) { + return executeOnExecutor(sDefaultExecutor, params); + } + //当然,AsyncTask 也支持并行执行,传入 AsyncTask.THREAD_POOL_EXECUTOR + public final AsyncTask 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; + //mFuture 是对任务队列(mWorker)的包装,可以通过 mFuture.get() 获得执行结果 + exec.execute(mFuture); + + return this; + } +``` + +我们知道,AsyncTask 的 execute 必须要在主线程中去执行,所以 onPreExecute() 也是在主线程中运行的。 + +再回头看看 AsyncTask 在执行完任务之后调用的 postResult 方法: + +```java + private Result postResult(Result result) { + @SuppressWarnings("unchecked") + Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, + new AsyncTaskResult(this, result)); + message.sendToTarget(); + return result; + } + + protected final void publishProgress(Progress... values) { + if (!isCancelled()) { + getHandler().obtainMessage(MESSAGE_POST_PROGRESS, + new AsyncTaskResult(this, values)).sendToTarget(); + } + } + + //任务执行过程中和执行完毕的包装类 + //mData 即执行的中间数据或执行结果 + //mTask 即当前 AsyncTask,用于调用 finsh 和 publishProgress 方法 + private static class AsyncTaskResult { + final AsyncTask mTask; + final Data[] mData; + + AsyncTaskResult(AsyncTask task, Data... data) { + mTask = task; + mData = data; + } + } + + private static class InternalHandler extends Handler { + public InternalHandler(Looper looper) { + super(looper); + } + + @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; + } + } + } + + private void finish(Result result) { + if (isCancelled()) { + onCancelled(result); + } else { + //重写的回调结果方法 + onPostExecute(result); + } + mStatus = Status.FINISHED; + } + + +``` + +##### 总结 + +首先说默认实现,即单线程线程池(SerialExecutor),它是系统全局的,也就是说每次实例化一个 AsyncTask 去执行任务的时候,都是先把任务添加到 ArrayDeque 队列尾部,然后等待上一个任务执行完毕之后,在去队列头部拿一个任务继续执行。 +任务都被封装成一个 Callable,即需要获取任务执行结果,执行的时候即是调用 doInBackground 方法,关于 onPostExecute 和 onProgressUpdate 即是在执行完毕和执行过程中通过 Handler 发消息来调用。 +AsyncTask 支持多线程并发,需要使用 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),传入 AsyncTask 中静态的线程池而已。 -[译文:Android中糟糕的AsyncTask](https://droidyue.com/blog/2014/11/08/bad-smell-of-asynctask-in-android/) \ No newline at end of file diff --git a/blogs/Android/Handler 消息机制.md b/blogs/Android/Handler 消息机制.md index 2b79d27..afaeec9 100644 --- a/blogs/Android/Handler 消息机制.md +++ b/blogs/Android/Handler 消息机制.md @@ -30,7 +30,7 @@ Android 应用是通过消息驱动运行的,在 Android 中一切皆消息, #### 基本使用 -基本使用不用多说,这里说一点就是尽量用第一种写法,第二种写法存在内存泄露。 +基本使用不用多说,有以下两种,但是都存在内存泄漏的情况,如何避免呢?可以通过静态内部类 + 弱引用来避免,文末常见问题汇总会有示例。 ```java private Handler mHandler = new Handler(new Handler.Callback() { diff --git a/blogs/Java/并发/线程、线程池.md b/blogs/Java/并发/线程、线程池.md index f7a5e39..76bd1a9 100644 --- a/blogs/Java/并发/线程、线程池.md +++ b/blogs/Java/并发/线程、线程池.md @@ -17,6 +17,8 @@ Java 线程、线程池 #### 前言 +线程、线程池这一块,如果牵扯到并发安全,那估计就不是一篇文章能写完的了,所以本篇文章侧重复习一下基础知识,同时学习了一下 Callable 和 Future,这也是为了阅读 AsyncTask 源码打下基础。又正赶上极客时间开了《并发编程实战》一栏,补充了线程生命周期、多线程线程分配数量策略等相关知识。 + #### 线程 ##### 线程的创建 @@ -132,6 +134,8 @@ public interface Future { ##### ThreadPoolExecutor +当你看到这里,我还是推荐你看:[Java并发编程:线程池的使用](https://www.cnblogs.com/dolphin0520/p/3932921.html) 这篇文章,这篇文章是我见过讲的最好的最仔细的!!! + ThreadPoolExecutor 类是线程池中最核心的一个类,它提供了四个构造方法: ```java @@ -207,7 +211,69 @@ public ThreadPoolExecutor(int corePoolSize, 由调度线程处理该任务。 +类继承关系如下: + +![](https://i.loli.net/2019/03/26/5c9981d2230e8.png) + +这里我就拿参考文章中的示例跑了一遍: + +```java +public class ThreadDemo { + public static void main(String[] args) throws ExecutionException, InterruptedException { + ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, + 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5)); + for (int i = 0; i < 15; i++) { + MyTask task = new MyTask(i); + executor.execute(task); + System.out.println("线程池中线程数目: " + executor.getPoolSize() + " 队列中等待执行的任务数: " + + executor.getQueue().size() + " 已执行完的任务数: " + executor.getCompletedTaskCount()); + } + executor.shutdown(); + } +} + +public class MyTask implements Runnable { + + private int taskName; + public MyTask(int taskName) { + this.taskName = taskName; + } + + @Override + public void run() { + System.out.println("正在执行 Task: " + taskName); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Task: " + taskName + " 执行完毕"); + } +} +``` + +当线程池中的线程数大于五时,便会将后续任务添加到任务等待队列中,当任务等待队列也满了之后,便会创建新的线程。如果将上面 for 循环改为 20,则会抛 RejectedExecutionException 任务拒绝异常。 + +不过在 Java 中,并不会推荐直接使用 ThreadPoolExcutor,而是使用 Executors 提供的几种静态方法: + +```java + public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } + public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + } +``` #### 参考 diff --git a/images/Java/线程池.png b/images/Java/线程池.png new file mode 100644 index 0000000..dd1f5e3 Binary files /dev/null and b/images/Java/线程池.png differ