文章目录

  • 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个部分组成:MessageHandlerMessageQueueLooper。其中MessageHandler在代码中将会频繁使用,而MessageQueueLooper则封装在Handler的等,下面我就对这4个部分进行一下简要的介绍:

  1. Message
    Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。我们可以使用到 Message的what字段来传递信息,除此之外还可以使用arg1arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
  2. Handler
    Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发岀的消息经过一系列地辗转处理后,最终会传递到Handler的 handleMessage()方法中。
  3. MessageQueue
    MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
  4. Looper
    Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

了解了MessageHandlerMessageQueue和以及Looper的基本概念后,我们再来把常规的异步消息处理的整个流程梳理一遍。

  1. 首先需要在主线程当中创建一个Handler对象,并重写 handleMessage()方法。
  2. 然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。
  3. 之后这条消息会被添加到MessageQueue的队列中等待被处理, 而Looper则会一直尝试从MessageQueue中取岀待处理消息,最后分发回Handler的 handleMessage()方法中。
  4. 由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI操作了。整个异步消息处理机制的流程示意图如图所示:
    每天学习一个Android中的常用框架——12.Handler_第1张图片

一条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的额外知识点比较多,包括HandlerThreadHadnler避免内存泄漏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 总结

Handler的使用比较简单,基本上分为以下三步:

  1. 创建Message对象,设置其what字段;
  2. 创建Handler对象,重写其handlerMessage()方法;
  3. 发送Message对象,使用Handler的sendMessage()方法,然后在handlerMessage()中进行相应的事件处理。

4.源码地址

AFL——Android框架学习

更多相关文章

  1. Android Cannas.drawTextView 方法坐标问题
  2. Android开机自启动程序设置及控制方法
  3. Android随笔之——Android时间、日期相关类和方法
  4. Android线程间通信机制
  5. Android NFC开发(二)——Android世界里的NFC所具备的条件以及使用
  6. Kernel.org 被黑,获取 Android 源码方法一则
  7. 关于android连续点击出现多个Activity界面的解决方法

随机推荐

  1. android webview夜间模式javascript代码
  2. Android SQLite数据库存储实现
  3. Android之Intent和常用Action
  4. Android调用系统自带的文件管理器进行文
  5. Android中使用Bezier曲线
  6. android编译错误--/usr/bin/ld: cannot f
  7. Android 学习 之 TextView结合SpannableS
  8. android 浮动在activity 上的button
  9. Android 截图程序实现 需要root权限
  10. android studio更新的时候出现Connection