本文我将从Handler是什么、有什么、怎们用、啥原理,四个方面去分析。才疏学浅,如有错误,欢迎指正,多谢。
1.是什么
因为Android系统不允许在子线程访问UI组件,否则就会抛出异常。所以咱们平实用的最多的可能是在子线程将更新UI的任务传达给UI线程去执行。但是别误会,它绝不仅仅是用来在子线程更新UI。Handler可以将任何线程的任务切换到它所在的线程去执行。
总体的流程其实就是: 初始化Handler并初始化CallBack重写handleMessage方法用来接收消息。handler的sendMessage发送消息。嗯嗯看起来好像就这么简单,我们实际用一下。
2.有什么
Handler的运行机制要依赖其他三个重要的类,分别是Message、MessageQueue、Looper。
2.1 Handler
最重要的作用就是,负责发消息和收消息。
2.2 Message
Message一看便知,是消息,消息机制肯定离不开消息这个载体啊,Messege就是消息本息。
它包含几个比较重要的成员变量what、arg1、arg2、都是int类型,what我们一般用来区分不同的消息;obj是个Object可以传递对象,一般传递数据主要在这个里面;还有target,它是Handler类型的,我们能接收到消息target功不可没《划重点》。
创建Message对象可以直接new Message() 也可以通过Message.obtain()获取,建议使用后者。然后通过handler的sendMessage(message)就把消息发出去了。上面提到的几个变量我们可以给他们赋值也可以不赋值,但是建议最少给what赋个值,如果都不赋值收到多个消息时会无法区分甲乙丙丁谁是谁,仅一条消息时您随意。发送消息还有sendEmptyMessage空消息不用message、sendMessageDelayed延时消息、sendEmptyMessageDelayed延时空消息、sendEmptyMessageAtTime等。
2.3 MessageQueue
MessageQueue消息队列专门用来存储消息,起名稍微有点误导性,它数据结构不是一个队列而是一个单列表。
此刻先记住他有两个重要的方法enqueueMessage和next(),前者用来存储消息后面用来从中取出消息并发送给handler的handlerMessage,注意这里的next是MessageQueue中方法和上面提到的Message类的next变量别混淆。
MessageQueue不生产消息,是个仓库管理员,只负责入库和出库。
看看它为啥是个单链表吧
链表就是对象的循环调用。当前要处理的messge是放到MessageQueue的mMessages变量,MessageQueue的mMessages变量是个Message,而Message类有一个next变量也是Message类型,同时这个Message类型的next变量又有自己的next变量,层层嵌套。后面说原理时具体分析。先上图,内容太多没有截取完整但能表达清楚关系:这里是使用下面3.1中方式一发送消息的断点截图。
2.4 Looper
Looper 负责取消息并发给handler的handlerMessage方法,核心方法是loop()它会调用MessageQueue的next()方法取消息并调用Handler的dispatchMessage(msg)将消息发送给handleMessage回到方法 ;
因为使用Handler必须要有looper因此它还提供了prepare()方法可以帮你实例化一个looper并存在当前线程的ThreadLocal中,因为你当前线程可能不是在主线程但你又要在主线程接收消息测试可以调用另一个方法getMainLooper,他可以为你提供应用主线程的looper,它的注释是这样写的:Returns the application's main looper, which lives in the main thread of the application.
3.怎么用
这里我根据初始化handler对象的不同区分了五种使用场景,后续也都会以方式一、方式二、方式三、方式四、方式五来区分。
- Handler发送Message
- 方式一 自定义一个TestHandler类继承Handler,并使用弱引用,避免内存泄漏,否则会一片黄色报警很不美观,里面写了两个构造方法分别是空参的Handle()和Handler(@NonNull Looper looper),实现主线程和子线程都能发消息,其实还有其他的构造方法我们后面分析。
//插入方式一对应的代码
- 方式一 自定义一个TestHandler类继承Handler,并使用弱引用,避免内存泄漏,否则会一片黄色报警很不美观,里面写了两个构造方法分别是空参的Handle()和Handler(@NonNull Looper looper),实现主线程和子线程都能发消息,其实还有其他的构造方法我们后面分析。
先定义TestHandler
public static class TestHandler extends Handler{private final WeakReference<Activity> weakReference;private final HandlerActivity activityWeak;//给方式一用public TestHandler(HandlerActivity activity){super();weakReference = new WeakReference<>(activity);activityWeak = (HandlerActivity) weakReference.get();activityWeak.handlerTv4.setText("已赋值");}//给方式三用public TestHandler(HandlerActivity activity,Looper looper){super(looper);weakReference = new WeakReference<>(activity);activityWeak = (HandlerActivity) weakReference.get();}@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);activityWeak.doMessage(msg);}
}
//初始化handler
testHandler = new TestHandler(this);
方式一发消息
// Message message1 = new Message();Message message1 = Message.obtain();message1.what = WHAT1;testHandler.sendMessage(message1);Message message2 = Message.obtain();message2.what = WHAT2;message2.obj = "空消息";testHandler.sendMessage(message2);Message message3 = Message.obtain();message3.what = WHAT3;message3.obj = "延时消息";testHandler.sendMessageDelayed(message3, DELAYTIME_1600);Message message4 = Message.obtain();message4.what = WHAT4;message4.obj = "延时空消息";testHandler.sendMessageDelayed(message4, DELAYTIME_1600);Message message5 = Message.obtain(); message5.what = WHAT5;message5.obj = "延时空消息";// 这个也是延时消息,在第二个参数后发出,但他是相较于最近一次开机时间的,因此基本桑拿是秒发,而且还会插队到其他消息前面,改成90000000L就一时半会收不到了。从testHandler.sendEmptyMessageAtTime(WHAT5,DELAYTIME_1600);
方式二
直接传入一个 CallBack,Handler(@Nullable Callback callback),CallBack可以直接new也可以改变成把CallBack单独提出来,效果一样
testHandler2 = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message message) {doMessage(message);return false;}
});
方式三
Handler(@NonNull Looper looper),传入一个looper,这样就可以在任何线程创建Handler了,我这里用的主线程的Looper所以在子线程这样写就可以把任务发到主线程去执行了。
为了验证传入Looper真实有效特意在子线程发送的。
new Thread(new Runnable() {@Overridepublic void run() {//方式三TestHandler testHandler3 = new TestHandler(HandlerActivity.this, Looper.getMainLooper());Log.e(TAG, "onClick: handlerTv3 ThreadId="+ Thread.currentThread().getId() );Message message1 = Message.obtain();message1.what = WHAT1;testHandler3.sendMessage(message1);}}).start();
用主线程创建的handler,用来子线程发消息OK,正常就这么用;
用子线程创建的handler如果没有设置looper就会在收不到消息并且报一个错:This is not main thread, and the caller should invoke Looper.prepare() and Looper.loop()called byandroid.os.Handler.<init>:122 com.example.testdemo3.activity.HandlerActivity$TestHandler.<init>:170 com.example.testdemo3.activity.HandlerActivity$4$1.run:151 java.lang.Thread.run:929 <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack>
创建handler实例时传入主线程的Looper.getMainLooper()就能在主线程收到
方式四
传入Looper和Handler.Callback
//有系统版本限制
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {Message message = Message.obtain();message.what = 5;//方式四Handler handler = Handler.createAsync(Looper.myLooper(), new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {Log.e(TAG, "createAsync-handleMessage: what= "+msg.what );return false;}});}
方式五
它不是一种实例化Handler的方式,方便下文引用它因此请他入列位列末席。一种特殊的发消息方式,是post一个Runnable。它基于以上四种方式任何一个,只是改变了发送方式采用了post但后面实际也会转成sendMessagexx方法(它也有postDelayed等延时方法,使用方法类似请各位大佬自行查看源码),这是在post方法的run回调方法里接收,但没法区分是哪个
testHandler.post(new Runnable() {@Overridepublic void run() {Log.e(TAG, "run: 收到消息但不知道是什么" );}
});
小贴士:
提示一:同一个handler多次发送同一条消息
handler.sendMessage(message);
handler.sendMessage(message);
如果同一个handler发送同一条消息连续发送两遍会引发闪退,下面的报错:
java.lang.IllegalStateException: { when=0 what=5 target=android.os.Handler } This message is already in use.
提示二:
上面方式三在子线程发消息就是调用了Looper.getMainLooper()这里可能比较好奇,既然使用Handler必须要有一个Looper那主线程的looper是哪来的? 原来在应用刚启动时在ActivityThread的main方法中已经调用了。
prepareMainLooper方法会调用prepare()方法可以帮你实例化一个looper并存在当前线程的ThreadLocal中,并且还有贴心的存在了Looper中提供了getMainLooper供人调用,真相大白了。同样looper.prepare()也会在threadlocal存一个looper后面mylooper()方法会将其取出来使用。
提示三:
一个线程只能有一个Looper,在创建looper对象时如果超过一个就会抛异常Only one Looper may be created per thread,因此不要多次调用prepare方法创建looper对象。
为了方便阅读将文章分为《Android-Handler详解_使用篇》和《Android-Handler详解_原理解析》两篇.