这是我在学习过程中总结的知识
目的是希望日后回来看或者需要用的时候可以 一目了然 # 的回顾、巩固、查缺补漏
不追求详细相当于书本的精简版或者说是导读(想看详细的直接对应翻书),但会尽力保证读者都能快速理解和快速使用(随理解加深会总结的更加精简),但必要时会附上一些较详细解释的链接
脚注是空白的:表示还没弄懂的知识,了解后会添加
除了Thread还有其他线程
· AsyncTask:底层用到了线程池和Handler,方便开发者在子线程中更新UI
· IntentService:底层直接用到了线程,内部采用HandlerThread,更方便执行后台任务且不容易被杀死
· HandlerThread:底层直接用到了线程,是一种消息循环的线程,内部可以使用Handler
线程过多时,为了防止一个进程频繁地创建和销毁线程,我们采用线程池来缓存一定数量的线程.线程池来自Java,主要通过Executor来派生特定类型的线程池
11.1 主线程和子线程
- 主线程:不进行耗时的操作以保证响应速度,主要运行四大组件以及处理它们和用户的交互
- 子线程:执行耗时任务,如网络请求、I/O操作等
11.2 Android中断的线程形态
11.2.1 AsyncTask
- 不同的API版本AsyncTask具有不同表现
- AsyncTask是一种轻量的异步任务类,可更新UI
- 可以更方便地执行后台任务以及在主线程中访问UI,但还是不能耗时
- 耗时任务推荐使用线程池
AsyncTask的声明
//Params表示参数的类型、Progress表示后台任务的执行进度类型、Result表示后台任务的返回结果类型
public abstract class AsyncTask<Params,Progress,Result>
AsyncTask的核心方法
(1)onPreExecute,在主线程中执行,在异步任务执行之前会被调用来做准备工作
(2)doInBackground(Params…params),在线程池中运行,用于执行异步任务,然后通过 publishProgress 方法来更新任务进度,publishProgress 调用 onProgressUpdate 方法,再返回计算结果给 onPostExecute
(3)onProgressUpdate(Progress values),在主线程中执行,后台任务执行进度改变时被调用
(4)onPostExecute(Result result)在主线程中执行,在异步任务执行之后,此方法返回后台任务返回值
(5)onCancelled,主线程中执行,当异步任务被取消时,会调用该方法,这时onPostExecute将不会被调用
在书P393中有一个使用AsyncTask的例子,大致思路是
- doInBackground 用来执行具体的下载任务并通过 publishProgress 方法来更新下载的进度,同时还要判断下载是否被外界取消了
- 下载任务完成后 doInBackground 会返回结果,即下载的总字节数
- doInBackground 运行在线程池, onProgressUpdate(随着publishProgress被调用而调用)用于更新界面中的下载进度,运行在主线程
- 最后onPostExecute被调用,也是运行在主线程,在界面上提示下载完成信息
AsyncTask是用的条件限制
(1)AsyncTask的类必须在主线程中加载,这在Android4.1以上已经自动完成了
(2)AsyncTask的对象必须在主线程中创建
(3)execute方法必须在UI线程调用
(4)不要在程序中调用 doInBackground 等那4个方法
(5)一个AsyncTask对象只能执行一次(调用一次execute方法)
11.2.2 AsyncTask的工作原理
从它的execute方法开始分析,execute又会调用 executeOnExecutor 方法
一个进程中的所有AsyncTask全部在这个串行的线程池(sDefaultExecutor)中排队执行
在executeOnExecutor方法中,AsyncTask的onPreExecute方法最先执行,然后线程开始执行
从 SerialExecutor 的实现跨越分析AsyncTask的排队执行过程.首先系统把AsyncTask的Params参数封装为FutureTask对象(类似Runnable),然后 SerialExecutor 的execute把FutureTask入队,再通过shceduleNext方法串行的执行下一个AsyncTask任务
AsyncTask中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTEO)和一个Handler(InternalHandler)
SerialExecutor用于任务的排队
THREAD_POOL_EXECUTEO用于真正地执行任务
InternalHandler用于将执行环境从线程池切换到主线程
AsyncTask默认是串行执行任务的(Android3.0以上的话),我们可以使用executeOnExecutor的方法来实现并行
11.2.3 HandlerThread
HandlerThread继承了Thread,是一种可以使用Handler的Thread
它的实现是在run方法中通过Looper.prepare来创建消息队列,然后开启消息循环
普通的Thread主要用于在run方法中执行一个耗时任务
HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式在通知它执行一个具体的任务
11.2.4 IntentService
· 它继承了Service并且它是一个抽象类
· 可执行后台耗时任务,执行后它会自动停止
· 优先级高不容易被杀死
· 它的onCreate方法中封装了Handler和HandlerThread
11.3 Android中的线程池
线程池的好处
- 避免因为线程的创建和销毁所带来的性能开销
- 有效控制线程池的最大并发数,避免大量线程之间因为互相抢占系统资源而导致的阻塞现象
- 提供定时执行以及指定间隔循环执行等功能
线程池的实现为ThreadPoolExecutor
线程池主要分为4类,是通过Executors所提供的工厂方法来得到的
11.3.1 ThreadPoolExecutor
它的构造方法的参数解释
corePoolSize
线程池的核心线程数,默认核心线程一直存活
如果设置allowCoreThreadTimeOut为true,那么核心线程会有超时策略,时间由keepAliveTime指定
maximumPoolSize
可容纳最大线程数
keepAliveTime
非核心线程的超时时长
unit
用于指定keepAliveTime参数的时间单位,TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES
workQueue
任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
threadFactory
线程工厂,为线程池提供创建新线程的功能,它是一个接口,只有一个方法:Thread newThread(Runnable r)
ThreadPoolExecutor执行任务时大致遵循如下规则
- 线程池中的线程数量<核心线程数量,则直接执行
- 线程池中的线程数量>=核心线程数量,任务被插入到任务队列等待执行
- 如果任务队列已满,在不超过线程池规定最大值的条件下,启动一个非核心线程来执行
- 已经超过规定最大值,调用RejectedExecutionHandler的rejectedExecutiom来拒绝任务
可以通过查看AsyncTask中的线程池配置来大致了解
11.3.2 线程池的分类
本节介绍Android中最常见的四类具有不同功能特性的线程池,它们都直接或者间接通过配置ThreadPoolExecutor来实现自己的功能特性
1. FixedThreadPool
通过Executors的newFixedThreadPool来创建,
是一种线程固定的线程池,
只有核心线程没有超时机制,
任务队列没有大小限制
更加快速响应外界的请求
2. CachedThreadPool
通过Executors的newCachedThreadPool来创建,
线程数量不定的线程池
只有非核心线程,最大线程数为Integer.MAX_VALUE(相当于任意大)
超时时长为60s
任何任务都会被立即执行
比较适合执行大量的耗时较少的任务
3. ScheduledThreadPool
通过Executors的newScheduledThreadPool来创建,
核心线程数固定
非核心线程数没有限制,闲置时被立即回收
用于执行定时任务和具有固定周期的重复任务
4. SingleThreadPool
通过Executors的newSingleThreadPool来创建,
只有一个核心线程,确保所有任务按顺序执行
不需要处理线程同步问题