Android-Handler详解_使用篇

先说一下我理解的Handler消息机制:以寄快递为例,我是Handler、快递员是Looper、商品仓库是MessageQueue、Messge是我下单消息寄出的物品。放假我Handler把电脑从公司寄回老家,我填写老家的地址就是声明handleMessage方法、我下单就是sendMessage我把电脑送到快递公司仓库MesssageQueue、然后经过快递公司内部流转,我老家的快递员Looper从仓库MessageQueue取出商品,根据地址送到我老家handleMessage方法。本文我将从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)就把消息发出去了。上面提到的几个变量我们可以给他们赋值也可以不赋值,可以是发一个没有赋值任何内容的空消息甚至干脆不添加消息对象,比如sendEmptyMessage以及其他几个带有Empty的发消息的方法都不需要传入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中方式一发送消息的断点截图。在一个点击事件里将通过handler发了五个消息,可以看出根节点是一个MessageQueue的对象mQueue,所有的消息都依附在它Message类型的mMessages变量,后发的消息是挂到前一条消息的next变量,messge5比较特殊因为他的延时是相较于最近一次开机时间的因此会插队到最前面。

2.4 Looper

Looper 负责取消息并发给handler的handlerMessage方法,核心方法是loop()它会调用MessageQueue的next()方法取消息并调用Handler的dispatchMessage(msg)将消息发送给handleMessage回调方法 ;

上面说了Handler可以将任何线程的任务切换到它所在的线程去执行,但是Handler必须要有looper才可以工作。

因为使用Handler必须要有looper因此它还提供了prepare()方法可以帮我们实例化一个looper并存在当前线程的ThreadLocal中,因为你当前线程可能不是在主线程但你又要在主线程接收消息测试可以调用另一个方法getMainLooper,他可以为你提供应用主线程的looper,它的注释是这样写的:Returns the application's main looper, which lives in the main thread of the application.

3.怎么用

这里我根据初始化handler对象的不同区分了五种使用场景,后续也都会以方式一、方式二、方式三、方式四、方式五来区分。

  1. Handler发送Message
    1. 方式一  自定义一个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();
    }
    @Override
    public 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() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        doMessage(message);
        return false;
    }
});


方式三

Handler(@NonNull Looper looper),传入一个looper,这样就可以在任何线程创建Handler了,我这里用的主线程的Looper所以在子线程这样写就可以把任务发到主线程去执行了。

为了验证传入Looper真实有效特意在子线程发送的。

                        new Thread(new Runnable() {
                    @Override
                    public 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() {
        @Override
        public 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() {
    @Override
    public 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,在创建looper对象时如果超过一个就会抛异常Only one Looper may be created per thread,因此不要多次调用prepare方法创建looper对象。

为了方便阅读将文章分为使用篇和源码解析两篇,请进入下一篇《Android-Handler详解_原理解析》

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Handler机制是Android中用于实现线程间通信的一种机制。它的主要原理是通过消息队列(MessageQueue)和消息循环(Looper)来实现。 在Android中,每个线程都有一个消息队列,用于存放待处理的消息。当线程需要发送消息时,可以通过Handler将消息发送到目标线程的消息队列中。由于每个线程都有自己的消息队列,因此可以实现不同线程之间的通信。 消息队列中的消息会按照先进先出的顺序被处理。当目标线程处于空闲状态时,它会不断地从消息队列中取出消息,并通过Handler的回调方法(Callback)对消息进行处理。处理完一个消息后,它会继续处理下一个消息,直到消息队列为空。 消息循环是实现消息队列轮询的一种机制。它会不断地从消息队列中取出消息,并将消息交给对应的Handler进行处理。当消息队列为空时,消息循环会进入休眠状态,等待新的消息到来。 通过Handler机制,我们可以在不同线程之间发送消息,实现线程之间的通信和任务的调度。它在Android开发中经常被用于更新UI、处理耗时操作等场景。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Handler原理解析(图文详解)](https://blog.csdn.net/haovin/article/details/89609688)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [AndroidHandler机制实现原理分析](https://download.csdn.net/download/weixin_38719890/15470797)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [android Handler机制原理解析(一就够,包你形象而深刻)](https://blog.csdn.net/luoyingxing/article/details/86500542)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值