Handler机制使用法则

本文详细介绍了Android应用中Handler机制的原理、用途及使用方法,包括如何发送和处理消息,以及如何在不同线程间传递数据。重点阐述了Handler在安排消息、实现基于时间的行为、以及与主线程交互等方面的运用。

转载请注明出处:http://blog.youkuaiyun.com/forevercbb/article/details/51295950

什么是Handler?

(翻译自Handler源码文档,英语渣,凑活看。英语好的可以看源文档。)
Handler允许你发送和处理Message,将Runnable对象和线程的MessageQueue相关联。每一个handler实例关联一个线程和线程的消息队列。当你创建一个新的Handler,它是绑定到创建它的线程以及消息队列中。从这一点上来看,它将传递消息和runnable对象,并且执行它们,因为它们来自消息队列。

Handler有两个主要的用途:1、安排消息和runable对象并且在将来的某个时间点执行它们。2、将一个行为放进队列,并将结果反馈到另一个线程中。

安排消息通过 post, postAtTime(Runnable, long), postDelayed, sendEmptyMessage, sendMessage, sendMessageAtTime, and sendMessageDelayed等方法实现。post允许将Runnable对象放入队列让接收到的消息队列进行调用。sendMessage允许你将一个Message对象放进消息队列,Message包含的bundle数据将在Handler的handleMessage方法中被处理。handleMessage的实现在Handler的子类中自定义。

当发送消息给Handler对象,你可以允许消息在消息队列就绪时立即被处理,或者指定一个延迟时间在它被处理之前,或者指定一个绝对的时间处理它。后面两种允许你实现超时、等待时间等基于时间的行为。

当你的应用创建一个进程,它的主线程运行一个消息队列,用来管理最高级别的应用对象,例如activities、broadcast receivers,和它们创建的任何windows。你可以创建你自己的线程,并通过Handler与应用的主线程进行交互。这依旧通过从你的新线程调用post或者senMessage方法实现。传递过来的Message和Runnable对象在消息队列中将在合适的时候被安排和执行。

Handler如何使用

Handler机制一般用来让子线程向主线程发送消息。大多用在子线程向主线程发送消息让主线程更新UI。

定义Handler类的子类,并实现handleMessage方法,在其中定义对消息的处理。

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            //针对不同的消息类型自定义处理
                switch (msg.what) {
                    case XXX:
                        break;
                    default:
                        break;
            }
        }
    }

实例化MyHandler类。一般在主线程。需要处理消息的线程。

private MyHandler mHandler = new MyHandler();

取得消息对象,设置消息对象的参数,并发送消息对象。一般在子线程,要通知或者给其它线程发送数据。

     Message msg = mHandler.obtainMessage();
        msg.what = tag;
        msg.obj = info;
        mHandler.sendMessage(msg);

另外可以根据需要选择其它方法,不难理解,不再赘述。

        mHandler.sendMessageAtTime();
        mHandler.sendMessageDelayed();

Message是什么

public final class Message implements Parcelable

Message是一个实现Parcelable接口的对象。Message的构造方法是公有的,但是一般情况下建议使用Message.obtainMessage()或者Handler.obtainMessage()方法从Message Pool中取得可循环的消息对象。Message的int成员变量what一般用来区分消息类型,每个Handler有自己的消息码的命名空间,因此不会和其它Handler有冲突。

arg1和arg2是低成本的,在消息只需要int值时,可以用arg1和arg2来替代setData()方法。

obj,obj是Object类型的对象,可以设置任意的非空的Parcelable对象。

也可以使用setData(Bundle data)给Message对象设置Bundle数据。

<think> 我们正在分析`~MessageHandler() = default;`这行代码的含义,并需要结合之前讨论的`explicit MessageHandler(rclcpp::Node::SharedPtr node);`(这是一个构造函数)来理解。在ROS 2的上下文中,`MessageHandler`类可能是一个自定义类,用于处理消息。 ### 1. 代码含义解析 ```cpp ~MessageHandler() = default; ``` - **`~MessageHandler()`** 这是析构函数的声明。析构函数在对象被销毁时自动调用,用于释放对象占用的资源(如内存、文件句柄、网络连接等)。 - **`= default;`** 指示编译器生成默认的析构函数实现。默认析构函数会: 1. 按声明顺序调用所有成员变量的析构函数 2. 如果基类存在,调用基类的析构函数 3. 不执行任何额外的操作 ### 2. 结合构造函数分析 之前讨论的构造函数: ```cpp explicit MessageHandler(rclcpp::Node::SharedPtr node); ``` - **设计意图** 1. **构造函数**:通过传入节点智能指针初始化对象 2. **析构函数**:使用`=default`表明类不需要特殊的资源清理逻辑 - **资源管理机制** - `node`参数是`shared_ptr`,采用引用计数管理节点生命周期 - 当`MessageHandler`对象销毁时: - 其成员变量(包括`node_`)会自动析构 - 如果`node_`是最后一个指向节点的`shared_ptr`,节点资源会被释放 ### 3. 使用场景分析 #### 场景1:隐式资源释放 ```cpp void process() { auto node = std::make_shared<rclcpp::Node>("demo_node"); MessageHandler handler(node); // 调用构造函数 handler.start(); } // 作用域结束,handler自动析构 -> node引用计数减1 ``` - 当`handler`离开作用域时,编译器生成的默认析构函数被调用 - 如果`node`没有其他引用,节点资源自动释放 #### 场景2:类成员管理 ```cpp class RobotController { MessageHandler msg_handler_; // 成员对象 public: RobotController(rclcpp::Node::SharedPtr node) : msg_handler_(node) {} // 委托构造 ~RobotController() = default; // 同样使用默认析构 }; ``` - `msg_handler_`的析构由`RobotController`的析构函数自动触发 - 无需手动编写清理代码 ### 4. 为什么使用`=default`? | 情况 | 使用场景 | 优势 | |------|----------|------| | **需要默认行为** | 类成员均为POD类型或自带RAII管理 | 避免空析构函数体带来的性能损耗 | | **兼容性要求** | 声明了移动构造/赋值等特殊成员函数 | 维持"三之法则"(Rule of Five)的完整性 | | **代码简洁性** | 无动态分配资源需要手动释放 | 减少冗余代码量 | ### 5. ROS 2中的实际意义 在ROS 2节点开发中: 1. **资源自动回收**:订阅者、发布者等资源在其所属的`rclcpp::Node`析构时自动清理 2. **异常安全**:即使发生异常,默认析构也能保证资源不泄漏 3. **生命周期同步**:确保消息处理器与节点生命周期一致 ### 相关问题 1. 什么情况下需要自定义析构函数而不是使用`=default`? 2. 在ROS 2节点中,如果`MessageHandler`持有非智能指针管理的资源该如何处理? 3. 默认析构函数与`virtual`析构函数在继承体系中的区别? 4. 如何验证ROS 2节点的资源是否被正确释放? > 引用说明:智能指针的资源管理机制遵循RAII原则[^1],`=default`的使用规范见C++ Core Guidelines C.80[^2],ROS 2节点的生命周期管理参见官方文档[^3]。 [^1]: C++ Core Guidelines R.11 [^2]: C++ Core Guidelines C.80 [^3]: ROS 2 Documentation - Lifecycle Nodes </think> ### C++中 `~MessageHandler() = default;` 的含义及使用场景 #### 1. **代码含义解析** ```cpp ~MessageHandler() = default; ``` - **`~MessageHandler()`** 这是**析构函数**的声明。析构函数在对象生命周期结束时自动调用,负责资源清理工作(如释放内存、关闭文件、取消ROS订阅等)。 - **`= default;`** 指示编译器生成**默认实现的析构函数**。这意味着: 1. 按成员声明顺序调用所有成员变量的析构函数 2. 如果存在基类,调用基类析构函数 3. 不执行任何额外的自定义操作 #### 2. **结合构造函数的完整类设计** ```cpp class MessageHandler { public: explicit MessageHandler(rclcpp::Node::SharedPtr node); // 构造函数 ~MessageHandler() = default; // 析构函数 // 其他成员函数... private: rclcpp::Node::SharedPtr node_; rclcpp::Subscription<...>::SharedPtr subscription_; }; ``` - **生命周期管理流程**: 1. **构造**:`MessageHandler handler(node);` → 调用自定义构造函数 2. **使用**:对象存活期间处理消息 3. **析构**:离开作用域时 → 调用默认析构函数 #### 3. **在ROS 2中的使用场景** ##### 场景1:自动资源释放 ```cpp void process() { auto node = std::make_shared<rclcpp::Node>("node"); { MessageHandler handler(node); // 构造 // 使用handler处理消息 } // 离开作用域 → 自动调用~MessageHandler()释放资源 } ``` - **智能指针成员**:`node_`作为`shared_ptr`会自动减少引用计数 - **ROS订阅者**:`subscription_`作为`SharedPtr`会自动取消订阅[^1] ##### 场景2:继承体系中的安全析构 ```cpp class AdvancedHandler : public MessageHandler { public: ~AdvancedHandler() { // 先执行子类清理 } }; ``` - 当`AdvancedHandler`对象销毁时: 1. 先调用`~AdvancedHandler()` 2. 自动调用`~MessageHandler() = default` 3. 最后调用基类析构 #### 4. **为何使用 `= default` 而非手动实现?** | 情况 | 使用`=default` | 手动实现析构函数 | |------|----------------|------------------| | **资源管理** | 成员均为智能指针时 | 需要手动释放原始指针 | | **代码维护** | 自动适应成员变化 | 需随成员变更修改代码 | | **性能** | 可能被编译器优化 | 可能有额外开销 | | **异常安全** | 自动提供基本保证 | 需自行确保安全性 | **ROS 2中的典型适用条件**: 1. 所有资源均通过智能指针管理(如`rclcpp::Subscription::SharedPtr`) 2. 无需要特殊清理的原始资源(如裸指针、文件句柄) 3. 类设计遵循RAII原则 #### 5. **何时需要自定义析构函数?** ```cpp ~MessageHandler() { // 手动取消ROS订阅(非智能指针) rcl_subscription_fini(&raw_subscriber_); // 释放原始内存 delete[] raw_buffer_; } ``` 需手动实现析构的情况: 1. 管理原始指针:`T* raw_ptr;` 2. 持有文件描述符:`int fd_;` 3. 使用C风格资源:`rcl_subscription_t raw_subscriber_;` 4. 需要记录特殊销毁日志 ### 相关问题 1. 在ROS 2节点中,如果类成员包含原始指针,如何安全实现析构函数? 2. `= default` 析构函数与移动语义(move semantics)有何关联? 3. 如何验证ROS 2订阅者在析构时确实被正确清理? 4. 在多线程ROS环境中,析构函数设计需要注意哪些线程安全问题? > 引用说明:智能指针的资源管理符合RAII原则[^1],默认析构函数的使用规范见C++ Core Guidelines C.80[^2],ROS 2节点的生命周期管理参见官方文档[^3]。 [^1]: ROS 2官方文档 - 订阅者生命周期 [^2]: C++ Core Guidelines C.80 [^3]: ROS 2 Documentation - Node Lifecycle
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值