天天看点

每天学习一个Android中的常用框架——12.Handler

文章目录

  • ​​1.简介​​
  • ​​2.特性​​
  • ​​3.演示​​
  • ​​3.1 集成​​
  • ​​3.2 创建Message​​
  • ​​3.3 创建Handler​​
  • ​​3.4 发送Message​​
  • ​​3.5 整体代码​​
  • ​​3.6 总结​​
  • ​​4.源码地址​​

1.简介

Handler,作为Android官方提供的一个专门处理消息处理的机制,严格来说其实并不算框架。但是为了更好地理解接下来将要学习的消息处理框架EventBus以及RxJava,我们还是需要先把官方提供的这套机制理解透彻。

Android开发者应该都了解,UI只可以在Android的主线程(UI线程)中进行更新,若想要在子线程中更新UI,就会抛出异常,因为Android的底层在绘制UI时会调用一个名为​

​checkThread()​

​的方法,通过这个方法,Android就会知道UI更新具体是在哪个线程进行的,如果是在子线程,则会抛出异常。

如果现在有个需求,例如在某个耗时操作中提供进度条的回显。为了解决这个需求,需要在子线程进行耗时操作的同时更新​

​ProgressBar​

​控件,但是这样就违反了Android系统中规定的不能在子线程中更新UI的规定。为了解决这个问题,Android就提供了一个异步消息处理工具:Handler,通过Handler,我们可以收发消息,从而切换主/子线程,达到在子线程中更新UI的效果。当然,除了这个功能外,Handler最大的作用还是解决多线程并发的问题。

作为一个典型的生产者——消费者线程模型,现在网上拥有很多详细介绍Handler的博文,而这篇博文以实用为主,介绍开发过程中与Handler相关的一些常用api和业务场景,关于理论的知识,可能会少介绍一些。如果读者想要了解更多关于Handler的理论知识,也可参考其他更加详细的博文。

2.特性

Android中的Handler机制主要由4个部分组成:​

​Message​

​​、​

​Handler​

​​、​

​MessageQueue​

​​和​

​Looper​

​​。其中​

​Message​

​​和​

​Handler​

​​在代码中将会频繁使用,而​

​MessageQueue​

​​和​

​Looper​

​则封装在Handler的等,下面我就对这4个部分进行一下简要的介绍:

  1. ​Message​

    ​​ Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。我们可以使用到 Message的​

    ​what​

    ​字段来传递信息,除此之外还可以使用​

    ​arg1​

    ​和​

    ​arg2​

    ​字段来携带一些整型数据,使用obj字段携带一个​

    ​Object​

    ​对象。
  2. ​Handler​

    ​​ Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的​

    ​sendMessage()​

    ​方法,而发岀的消息经过一系列地辗转处理后,最终会传递到Handler的 ​

    ​handleMessage()​

    ​方法中。
  3. ​MessageQueue​

    ​ MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
  4. ​Looper​

    ​​ Looper是每个线程中的MessageQueue的管家,调用Looper的​

    ​loop()​

    ​方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的​

    ​handleMessage()​

    ​方法中。每个线程中也只会有一个Looper对象。

了解了​

​Message​

​​、​

​Handler​

​​、​

​MessageQueue​

​​和以及​

​Looper​

​的基本概念后,我们再来把常规的异步消息处理的整个流程梳理一遍。

  1. 首先需要在主线程当中创建一个Handler对象,并重写​

    ​handleMessage()​

    ​方法。
  2. 然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。
  3. 之后这条消息会被添加到MessageQueue的队列中等待被处理, 而Looper则会一直尝试从MessageQueue中取岀待处理消息,最后分发回Handler的​

    ​handleMessage()​

    ​方法中。
  4. 由于Handler是在主线程中创建的,所以此时​

    ​handleMessage()​

    ​方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI操作了。整个异步消息处理机制的流程示意图如图所示:
  5. 每天学习一个Android中的常用框架——12.Handler

一条Message经过这样一个流程的辗转调用后,也就从子线程进入到了主线程,从不能更新UI变成了可以更新UI,整个异步消息处理的核心思想也就是如此。

而之前曾经使用到的​

​runOnUiThread()​

​方法其实就是一个异步消息处理机制的接口封装,它虽然表面上看起来用法更为简单,但其实背后的实现原理和这里的描述是一样的。

为了便于演示,这里只使用Handler中的一些常用的api来实现基本功能,若想要实现额外的功能可以参考CSDN上的其他博文,这里不再赘述。

3.演示

3.1 集成

Handler默认就是集成在Android SDK中的,所以这一步可以省略,你可以在代码中直接使用Handler来处理消息机制。

3.2 创建Message

在使用Handler来处理消息机制时,我们应该先获取到Message的对象实例。Message的创建总共有三种方式:

  1. 通过Message类的构造方法,即:​

    ​Message message = new Message();​

  2. 用过Message类的​

    ​obtain()​

    ​方法,即:​

    ​Message message = Message.obtain();​

    ​推荐使用这种方式来获取Message的对象实例,因为Message在回收时会被放入到一个对象池中
  3. 通过Handler类的​

    ​obtaionMessager()​

    ​方法,即:
//通过handler实例获取
Handler handler = new Handler();
Message message=handler.obtainMessage();      

但事实上,这个方法的底层调用了第二种方式的代码,Android源码如下:

public final Message obtainMessage(){
  return Message.obtain(this);
}      

大部分的场景下,我们都是使用第二种方式来实现Message的创建。

3.3 创建Handler

创建好Message之后,接下来就是需要创建Handler。创建Handler我们需要用构造方法来获取其实例,然后重写其中的​

​handleMessage()​

​方法,表示在接收到消息后进行相应的事件处理。一个常规的Handler代码如下:

private Handler mHandler = new Handler(){
        
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    tv_text.setText("TextView文本在子线程中被修改了!");
                    break;
                default:
                    break;
            }
        }
    };      

该Handler从​

​handleMessage()​

​​里的​

​msg​

​​参数中取出​

​what​

​​字段,即​

​msg.what​

​​。通过该属性值使用​

​Switch-Case​

​表达式来选择执行相应的逻辑。

3.4 发送Message

Handler创建好后,我们就需要使用Handler来发送Message,以此来通知Handler执行相应的逻辑处理。发送Message需要借助Handler类里的方法,其中主要的方法如下:

  1. 直接发送消息:​

    ​mHandler.sendMessage(message);​

  2. 延时1s后发送消息:​

    ​mHandler.sendMessageDelayed(message,1000);​

  3. 发送带标记的消息(需要内部创建message,并设置​

    ​msg.what = 0x1​

    ​​):​

    ​mHandler.sendEmptyMessage(0x1);​

  4. 延时1s后发送带标记的消息(需要内部创建message,并设置​

    ​msg.what = 0x1​

    ​​):​

    ​mHandler.sendEmptyMessageDelayed(0x1,1);​

  5. 移除标记为​

    ​0x1​

    ​​的消息:​

    ​mHandler.removeMessages(0x1);​

  6. 移除回调的消息:​

    ​mHandler.removeCallbacks(Runnable);​

  7. 移除回调和所有的message:​

    ​mHandler.removeCallbacksAndMessages(null);​

Handler的额外知识点比较多,包括​

​HandlerThread​

​​、​

​Hadnler避免内存泄漏​

​​、​

​runOnUiThread()​

​​以及Hander的​

​post()​

​方法等几个点。这里限于作者文笔浅陋不能很好地讲解这些知识点,就暂时不写出来了。等到作者重新组织好文思后,再来更新这篇博文。

3.5 整体代码

为了便于演示,这里只有一个名为MainActivity,其Java代码和布局文件分别如下:

package com.androidframelearn.evnet_handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView tv_text;

    private Button btn_send_message;

    private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    tv_text.setText("TextView文本在子线程中被修改了!");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化UI
        initUI();

        // 为按钮注册点击事件
        btn_send_message.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = Message.obtain();
                        message.what = 1;
                        mHandler.sendMessage(message);
                    }
                }).start();
            }
        });
    }

    /**
     * 初始化UI
     */
    private void initUI() {
        tv_text = findViewById(R.id.tv_text);
        btn_send_message = findViewById(R.id.btn_send_message);
    }
}      
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试用的文本!"
        android:textSize="20sp"/>

    <Button
        android:id="@+id/btn_send_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送消息"/>

</LinearLayout>      

3.6 总结

  1. 创建Message对象,设置其​

    ​what​

    ​字段;
  2. 创建Handler对象,重写其​

    ​handlerMessage()​

    ​方法;
  3. 发送Message对象,使用Handler的​

    ​sendMessage()​

    ​​方法,然后在​

    ​handlerMessage()​

    ​中进行相应的事件处理。

4.源码地址