handlerthread原理(handler threadlocal)
Android多线程的四种方式:Handler、AsyncTask、ThreadPoolExector、IntentService
? ? ?异步通信机制,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理。Handler不仅仅能将子线程的数据传递给主线程,它能实现任意两个线程的数据传递。
(1)Message
? ? Message 可以在线程之间传递消息。可以在它的内部携带少量数据,用于在不同线程之间进行数据交换。除了 what 字段,还可以使用 arg1 和 arg2 来携带整型数据,使用 obj 来携带 Object 数据。
(2) Handler
? ? Handler 作为处理中心,用于发送(sendMessage 系列方法)与处理消息(handleMessage 方法)。
(3) MessageQueue
? ? MessageQueue 用于存放所有通过 Handler 发送的消息。这部分消息会一直存放在消息队列中,直到被处理。每个线程中只会有一个 MessageQueue 对象
(4) Looper
? ? Looper 用于管理 MessageQueue 队列,Looper对象通过loop()方法开启了一个死循环——for (;;){},不断地从looper内的MessageQueue中取出Message,并传递到 Handler 的 handleMessage() 方法中。每个线程中只会有一个 Looper 对象。
? ? AsyncTask 是一种轻量级的任务异步类,可以在后台子线程执行任务,且将执行进度及执行结果传递给 UI 线程。
(1)onPreExecute()
? ??在 UI 线程上工作,在任务执行 doInBackground() 之前调用。此步骤通常用于设置任务,例如在用户界面中显示进度条。
(2)doInBackground(Params... params)
? ??在子线程中工作,在 onPreExecute() 方法结束后执行,这一步被用于在后台执行长时间的任务,Params 参数通过 execute(Params) 方法被传递到此方法中。任务执行结束后,将结果传递给 onPostExecute(Result) 方法,同时我们可以通过 publishProgress(Progress) 方法,将执行进度发送给 onProgressUpdate(Progress) 方法。
(3)onProgressUpdate(Progress... values)
? ? 在 UI 线程上工作,会在 doInBackground() 中调用 publishProgress(Progress) 方法后执行,此方法用于在后台计算仍在执行时(也就是 doInBackgound() 还在执行时)将计算执行进度通过 UI 显示出来。例如,可以通过动画进度条或显示文本字段中的日志,从而方便用户知道后台任务执行的进度。
(4)onPostExecute(Result result)
? ? 在 UI 线程上工作,在任务执行完毕(即 doInBackground(Result) 执行完毕)并将执行结果传过来的时候工作。
使用规则:
(1)AsyncTask 是个抽象类,所以要创建它的子类实现抽象方法
(1)AsyncTask 类必须是在 UI 线程中被加载,但在Android 4.1(API 16)开始,就能被自动加载完成。
(2)AsyncTask 类的实例对象必须在 UI 线程中被创建。
(3)execute() 方法必须是在 UI 线程中被调用。
(4)不要手动调用方法 onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()
(5)任务只能执行一次(如果尝试第二次执行,将抛出异常)。即一个AsyncTask对象只能调用一次execute()方法。
原理:
? ? ? ? ? 其源码中原理还是 Thread 与 Handler 的实现,其包含 两个线程池,一个 Handler,如下所示:
名称类型作用
SERIAL_EXECUTOR线程池分发任务,串行分发,一次只分发一个任务
THREAD_POOL_EXECUTOR线程池执行任务,并行执行,执行的任务由 SERIAL_EXECUTOR 分发
InternalHandlerHandler负责子线程与主线程的沟通,通知主线程做 UI 工作
? ? 一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。Executors利用工厂模式对ThreadPoolExecutor进行了封装。
Executors提供了四种创建ExecutorService的方法,他们的使用场景如下:
1. Executors.newFixedThreadPool()
? ? 创建一个定长的线程池,每提交一个任务就创建一个线程,直到达到池的最大长度,这时线程池会保持长度不再变化。
当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。
只有核心线程并且不会被回收,能够更加快速的响应外界的请求。
2. Executors.newCachedThreadPool()
? ? 创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活的回收空闲的线程,当需要增加时,它可以灵活的添加新的线程,而不会对池的长度作任何限制
? ? 线程数量不定的线程池,只有非核心线程,最大线程数为 Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则利用空闲的线程来处理新任务。线程池中的空闲线程具有超时机制,为 60s。
? ? 任务队列相当于一个空集合,导致任何任务都会立即被执行,适合执行大量耗时较少的任务。当整个线程池都处于限制状态时,线程池中的线程都会超时而被停止。
3. Executors.newScheduledThreadPool()
? ? 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。
? ? 非核心线程数没有限制,并且非核心线程闲置的时候立即回收,主要用于执行定时任务和具有固定周期的重复任务。
4. Executors.newSingleThreadExecutor()
? ? 创建一个单线程化的executor,它只创建唯一的worker线程来执行任务
? ? 只有一个核心线程,保证所有的任务都在一个线程中顺序执行,意义在于不需要处理线程同步的问题。
? ? 一般用于执行后台耗时任务,当任务执行完成会自动停止;同时由于它是一个服务,优先级要远远高于线程,更不容易被系统杀死,因此比较适合执行一些高优先级的后台任务。
使用步骤:创建IntentService的子类,重写onHandleIntent方法,在onHandleIntent中执行耗时任务
? ? 原理:在源码实现上,IntentService封装了HandlerThread和Handler。onHandleIntent方法结束后会调用IntentService的stopSelf(int startId)方法尝试停止服务。
? ? IntentService的内部是通过消息的方式请求HandlerThread执行任务,HandlerThread内部又是一种使用Handler的Thread,这就意味着IntentService和Looper一样是顺序执行后台任务的
(HandlerThread:封装了Handler + ThreadHandlerThread适合在有需要一个工作线程(非UI线程)+任务的等待队列的形式,优点是不会有堵塞,减少了对性能的消耗,缺点是不能同时进行多个任务的处理,需要等待进行处理。处理效率低,可以当成一个轻量级的线程池来用)
Android线程之HandlerThread
HandlerThread是Thread的一个子类,是Android中提供的另一种线程形态。
我擦,有线程、有looper这不正是我们当初声称在子线程中构建handler消息系统的所需要的吗?
那很明显,这个 HandlerThread 的目的就是让我们创建一个 Handler ,然后所有的任务操作成功的转给了 Handler 来处理完成。
但注意的是这个 Handler 的消息处理是运行在子线程中的。
在主线程中创建 handler ,简单的模拟一下 HandlerThread 工作原理
由于这个handler回调是运行在子线程中的,因此如果你想要更新UI可以借助主线程的默认的looper来实现,这个问题又愉快的转化到了子线程更新UI的问题。
HandlerThread其本质就是一个线程,只不过这个线程加入了Handler消息异步处理的机制。
那这与普通的创建线程的好处是什么呢?
HandlerThread原理与应用
?在理解了Handler的原理之后,我们知道在一个子线程中创建一个Handler不能缺少了Looper.prepare()和Looper.loop()两个方法,具体的原因这里不再赘述,不熟悉原理的可以先看下另一篇文章 Handler的原理解析 .
本篇文章主要是讲解HandlerThread的使用的。
?首先HandlerThread是继承于Thread类的,所以本质上HandlerThread就是一个线程,接下来就详细的去看一看,这是怎样的一个线程?
?首先,先看下它的构造函数:
?既然是线程,那么最重要的当然是run方法来,看完了run方法,相信你也就明白HandlerThread的用途了!
?怎么样,这是不是和我们之前在子线程中创建Handler一样,首先是Looper.prepare(),接着是new Handler(), 最后是Looper.loop()。等等,这里并没有创建Handler啊!别急,我们先一步一步地看看run方法再说为什么没有创建Handler。
?通过查找发现到一个getLooper()的方法,该方法返回了当前线程的mLooper对象,还记得Looper是在哪里进行赋值的吗?在线程的run方法里,所以当线程启动之后才能创建Looper并赋值给mLooper,这里的阻塞就是为了等待Looper的创建成功。同时该方法是用Public修饰的,说明该方法是提供外部调用的,Looper创建成功提供给外部使用。
?最后在对象销毁前,调用下面的方法退出Looper循环
?quit方法实际是调用MessagQueue的removeAllMessagesLocked,移除所有延迟和非延迟的消息,
?quitSafely方法调用的是removeAllFutureMessagesLocked方法,该方法只清除延迟的消息,非延迟的消息
还是会进行分发处理。
?HandlerThread分析完啦,是不是有点蒙,自始至终都没有出现Handler,HandlerThread要怎么用呢?
?下面我们就通过一个Demo来说明下HandlerThread是怎么用的?
Handler详解
Handler主要用于异步消息的处理: 有点类似辅助类,封装了消息投递、消息处理等接口。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
为什么要用handler?不用这种机制行不行?不行!android在设计的时候,就封装了一套消息的创建、传递、处理机制,如果不遵循这种机制,就没有办法更新UI信息,就会抛出异常信息。
android 为什么要设计只能通过handler机制更新UI?
最根本的目的是解决多线程并发温问题:
假设如果在一个activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会什么样子的问题?更新界面混乱。
如果对更新UI的操作都进行加锁处理的话又会产生什么样子的呢?性能下降。
出于对以上问题的考虑,Android给我们提供了一套更新UI的机制,我们只要遵循这个机制就可以了,根本不用去关心多线程并发的问题,所有的更新UI的操作,都是在主线程的消息队列中去轮训处理的。
handler的原理是什么?
一、Handler封装了消息的发送 (主要包括消息发送给谁)
1、内部包含一个消息对列,也就是MessageQueue,所有的handler发送的消息都走向这个消息对列。
2、Loopler.looper方法,就是一个死循环,不断地从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞
二、MessageQueue,就是一个消息对列,可以添加消息,并处理消息
三、handler也很简单,内部会跟Looper进行关联,也就是说在handler的内部可以找到Looper,找到Looper也就找到了MessageQueue,在handler中发送消息,其实就是向MessageQueue队列中发送消息
handler负责发送消息,Looper负责接收handler发送的消息,并直接把消息回传给
handler自己。MessageQueue就是一个存储消息的容器
打印的log日志如下:
当主线程handler中传入子线程的looper时,程序直接奔溃,报空指针错误,原因时存在多线程并发的问题,当两个线程在切换的时候,在程序运行到主线程中handler的创建时,传入了子线程的looper,而此时子线程中的looper还没有创建出来,所以会抛出空指针异常,那么这个问题怎么避免呢,就用到了HandlerThread。报错信息见下图: