转载请注明出处:https://blog.csdn.net/github_38372075/article/details/80534808
我的简书传送门
今天我们来了解一下Handler。Android中操作UI控件需要在主线程中进行,为了打破对主线程的依赖(将耗时操作在后台线程执行,而将执行结果在ui线程中操作ui显示),Android引入了Handler消息传递机制。
Handler
Handler有这样三个特点:
a.允许你去发送并处理一条与Runnable对象和MessageQueue相关联的消息。
b.每一个Handler实例都与一个单独的线程和该线程的MessageQueue相关。
c.当你创建一个新的处理程序时,它将绑定到正在创建的线程的线程/消息队列——从那个点开始,它将向该消息队列传递消息和runnables,并在它们从消息队列中释放时执行它们。
实际上,根据我的理解,handler就是我们在各线程间处理发送消息数据的一种机制,实现线程间切换的一种方式。
Handler有两个主要用途:
(1)将消息和runnables作为将来的某个点执行
。
(2)在不同的线程上执行要执行的操作
。
Handler线程模型
先介绍了一下Handler的基本概念,下面就来说一下handler线程模型与一些handler常用的函数以及相关的类。
* 我们都知道,android的UI操作必须要在主线程中进行,为什么? 因为在多线程中同时执行UI操作是不安全的
* 可以把全部的操作都放在主线程中么? 不可以,耗时操作需要在后台线程执行,避免ANR
也就是说,如果我们进行了耗时操作,如网络加载图片后,又想显示在我们的ImageView上,那么就需要进行线程间切换,使用handler将后台线程切换到主线程上后,进行UI操作。
Handler常用方法
构造方法
Handler是Android中实现线程间切换机制的类
* public Handler() 无参构造,直接new。
// 若只是执行runnable则不需要覆写handleMessage方法。因为执行runnable会自动忽略handleMessage方法本身。
Handler handler0 = new Handler();
// 如果我们要对发送的消息进行操作则需要覆写handleMessage方法。
Handler handler1 = new Handler(){
@Override
public void handleMessage(Message msg) {
// 获得消息后操作
}
}
- public Handler(Callback callback)
- public Handler(Looper looper)
- public Handler(Looper looper,Callback callback) 参数1,looper;参数2,callback回调函数,相当于实现handler抽象方法handleMessage(Message msg);。
public interface Callback {
public boolean handleMessage(Message msg);
}
调用函数
- post(Runnable r) 将Runnable添加到MessageQueue中。此处要强调一点,如果发送的是runnable则会忽略掉handleMessage方法的执行,即使是发送含有runnable的Message则也会忽略掉handleMessage方法的执行
public interface Callback {
public boolean handleMessage(Message msg);
}
- postAtTime(Runnable r, long updateMillis) 在指定的时间点运行runnable。
- postDelayed(Runnable r, long delayMillis) 在延迟一段时间后运行runnable。
// 此处两个方法执行runnable时机一致
handler.postDelayed(runnable,2000);
handler.postAtTime(runnable,System.currentTimeMillis() + 2000);
- postAtFrontQueue(Runnable r) 将Runnable放在队列最前端执行
- sendEmptyMessage(int what) 发送一个空消息,参数为int型what(消息执行标识)。
- sendMessage(Message msg) 发送消息。
- sendMessageAtTime(Message msg,long updateMillis) 在指定时间点发送消息。
- sendMessageDelayed(Message, long delayMillis) 在延迟一段时间后发送消息。
- sendMessageAtFrontOfQueue(Message msg) 将message放在消息队列的最前面发送。
sendMessage方法与post方法最后执行handleMessage方法或执行runnable的线程就是Handler的创建线程。
取消任务
- removeCallbacks(Runnable r) 移除当前消息队列中runnable == r的消息。
- removeCallbacks(Runnable r ,Object token) 移除当前消息队列中target == handler,runnable == r,Object == token 的消息。
- removeMessages(int what) 取消所有标识为what的消息。
- removeMessages(int what ,Object token) 取消含有token对象的被what标识的消息。
- removeCallbacksAndMessages(Object token) 取消含有token的全部消息。
有关Handler常用类,四个组成部分
- Message:消息,被传递和处理的数据。其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Message作为传递的消息与Handler密不可分,所以此处我们稍微展开讲解一下Message属性与获得方式:
Message的属性):
- public int what 传递的消息执行标识,用于标记不同的消息。
- public int arg1 类似与bundle来进行携带int类型的数据。
- public int arg2 如果一个arg1还不够用再来个arg2。
- public Object object 任意对象传递容器。类似于bundle,用于跨进程专递对象
- Runnable callback 执行的runnable对象。
- long when 发送的时机。
- Handler target 所依附的handler,可以理解为通过这个target,这个消息会找到它该去的地方,然后执行对消息的操作也就是handleMessage方法。
Message的获得(通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源): - Message msg0 = new Message();
- Message msg1 = Message.obtain();
- public static Message obtain(Handler h)
- public static Message obtain(Handler h, int what)
- public static Message obtain(Handler h, int what, Object obj)
- public static Message obtain(Handler h, int what, int arg1, int arg2)
- public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) 参数1:targetHandler,参数2:what标识符,参数3:int型数据,参数4:int型数据,参数5:Object对象。
- public static Message obtain(Message orig) 相当于拷贝某个Message而获得Message。
- public static Message obtain(Handler h,Runnable callback) 传入targetHandler与需要回调执行的runnable。
- Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。Handler类的主要作用:(有两个主要作用)
a. 在工作线程中发送消息;
b. 在主线程中获取、并处理消息。 - MessageQueue:消息队列,本质是一个数据结构,用来存放Handler发送过来的消息,并按照FIFO规则(先进先出)执行。当然,存放Message并非实际意义的保存,而是将Message串联起来,等待Looper的抽取。
- Looper:消息泵或循环器,不断从MessageQueue中抽取Message。因此,一个MessageQueue需要一个Looper。
- Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
此处我们画一个简易图来逐步了解Handler的运行机制。图中Handler(左右两个handler为同一个handler)可以与MessageQueue以及Looper在同线程中,也可在不同线程中。
Handler进程间切换举例
到这里,我想我们脑中有了一个对Handler大致的理解,渐渐的看清了它的样子,那么接下来,我们来写一些代码,来看看handler究竟是怎样运行,工作的。
主线程创建handler
为了节约版面,我们不列举每一种方法,只举例有代表性的方法,其他的都会提交到我的github中Handler传送门,大家有需要可以去下载研究。
* 我们先在主线程中创建handler而在非主线程中来调用post(Runnable r),观察是否最后runnable运行的线程为主线程(代码段中post(View v)方法为button点击调用函数)。
public void post(View v) {
Log.e("tag", " ------>>> " + "点击Post按钮 " + getThreadName());
// 在主线程创建handler
final Handler handler = new Handler();
// 创建一个非主线程的新线程
Thread thread = new Thread(
new Runnable() {
@Override
public void run() {
Log.e("tag", " ------>>> " + "执行非主