面试总结(1):HandlerThread

2024-06-04 16:58
文章标签 面试 总结 handlerthread

本文主要是介绍面试总结(1):HandlerThread,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

面试总是让人既紧张又兴奋,尤其是百度这样大公司,总是想证明自己的能力,害怕但是又期望能问出自己的不足,真是一次难得面试体验,所以回来赶紧把没回答出来的知识点学习一下。

Handler和Thread之间的关系和用法,我们都很熟悉,那么你了解HandlerThread吗?

正文

当我听到这个问题的时候,是有一点蒙的,因为我确实没用过,可能是平时看到或者听到过。那什么是HandlerThread呢?

顾名思义,就是一个含有Handler的Thread,他是一个线程。子线程就可以用用来处理异步任务,耗时操作。

用法:

HandlerThread teacherThread = new HandlerThread("teacher");
teacherThread.start();
teacherHandler = new Handler(teacherThread.getLooper());

很简单,创建一个HandlerThread对象,然后把这个线程的Looper绑定到Handler里面去,那么Handler里的任务就会在这个Thread中执行。

好处

经过我仔细的思考和试验,主要有以下几点:

1、模块划分清晰,把耗时任务细分,便于维护管理。
2、复用性很好,避免创建临时性的线程,消耗系统资源。
3、有利于多线程通信。
4、让UIhandler更专注于更新界面,优化界面的流畅度。

Demo

多说无用,赶紧写点东西实际感受一下。

我们来模拟一个考场的情景:

/**
* 线程池* */
private ExecutorService threadPool = Executors.newSingleThreadExecutor();/**
* 更新UI的Handler
* */
private Handler mainHandler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case 0:textView.setText("考试开始,老师开始发放试卷...");break;case 1:textView.setText("发放试卷完毕,学生开始答题...");break;case 2:textView.setText("考试时间到,老师收试卷...");break;case 3:textView.setText("考试结束...");break;}}
};/**
* 线程池开启任务
*/
threadPool.execute(new Runnable() {@Overridepublic void run() {mainHandler.sendEmptyMessage(0);// 老师发卷...mainHandler.sendEmptyMessage(1);// 学生答题...mainHandler.sendEmptyMessage(2);// 答题结束...mainHandler.sendEmptyMessage(3);}
});

大概就是这样一个流程,如果是一般写法,首先创建一个线程池开启线程,线程里面开始处理各种各样的任务,到一定的阶段,就更新UI的显示状态。

但是如果这个线程任务变得越来越复杂,代码越来越长,维护性也会变差,所以就得重新设计一下这个任务。

从面向对象的编程思想来看,首先我们可以把这个任务划分为两个角色:

老师:发送试卷,回收试卷。
学生:答题。

老师发送完试卷,通知学生答题,学生答题结束,通知老师回收试卷。

ok,分析结束,就先实现老师和学生的Handler:

teacherHandler = new Handler(teacherThread.getLooper()){@Overridepublic void handleMessage(Message msg) {switch (msg.what){// 接收到开始指令,老师开始发放试卷case 0:// 更新UImainHandler.sendEmptyMessage(0);sleep();// 发送试卷结束,学生开始答题studentHandler.sendEmptyMessage(0);break;// 学生答题结束,老师开始收试卷case 1:// 开始收卷mainHandler.sendEmptyMessage(2);sleep();// 考试结束mainHandler.sendEmptyMessage(3);break;}}
};studentHandler = new Handler(studentThread.getLooper()){@Overridepublic void handleMessage(Message msg) {// 接收到开始指令,老师开始发放试卷mainHandler.sendEmptyMessage(1);sleep();// 发送试卷结束,学生开始答题teacherHandler.sendEmptyMessage(1);}
};// mainHandler 代码不变
...

老师和学生通过handleMessage()来接受操作指令,内部实现具体的功能逻辑。

搞定了两个角色类,剩下的就是onCreate中去初始化线程了:

public class MainActivity extends AppCompatActivity {private TextView textView;/*** 处理老师和学生操作的线程* */private HandlerThread teacherThread, studentThread;private Handler teacherHandler, studentHandler;/*** 更新UI的Handler* */private Handler mainHandler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case 0:textView.setText("考试开始,老师开始发放试卷...");break;case 1:textView.setText("发放试卷完毕,考试开始答题...");break;case 2:textView.setText("考试时间到,老师收试卷...");break;case 3:textView.setText("考试结束...");break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = (TextView) findViewById(R.id.textView);initHandlerThread();teacherHandler.sendEmptyMessage(0);}/*** 初始化HandlerThread* */private void initHandlerThread() {Log.e("lzp", "mainLooper:" + getMainLooper().toString());teacherThread = new HandlerThread("teacher");teacherThread.start();Log.e("lzp", "teacherLooper:" + teacherThread.getLooper().toString());teacherHandler = new Handler(teacherThread.getLooper()){@Overridepublic void handleMessage(Message msg) {switch (msg.what){// 接收到开始指令,老师开始发放试卷case 0:// 更新UImainHandler.sendEmptyMessage(0);sleep();// 发送试卷结束,学生开始答题studentHandler.sendEmptyMessage(0);break;// 学生答题结束,老师开始收试卷case 1:// 开始收卷mainHandler.sendEmptyMessage(2);sleep();// 考试结束mainHandler.sendEmptyMessage(3);break;}}};studentThread = new HandlerThread("student");studentThread.start();Log.e("lzp", "studentLooper:" + studentThread.getLooper().toString());studentHandler = new Handler(studentThread.getLooper()){@Overridepublic void handleMessage(Message msg) {// 接收到开始指令,老师开始发放试卷mainHandler.sendEmptyMessage(1);sleep();// 发送试卷结束,学生开始答题teacherHandler.sendEmptyMessage(1);}};}private void sleep(){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}

为了便于显示,我没有把teacherHandler和studentHandler 两个类独立出来,如果独立出来,MainActivity的代码将会变得更加简洁易看。

我还打印了UI线程的Looper,和两个HandlerThread的Looper:

这里写图片描述

三个线程的Looper是不一样的,的确我们的teacherHandler和studentHandler是运行在新线程中的。而且老师和学生之间的通信变得很简单,直接通过Message把需要的数据携带进去就OK了。

使用结束记得退出HandlerThread:

public void release(){getLooper().quit();
}

总结

HandlerThread的使用方法就是这个样子,他不需要我们去专注Thread的管理,把更多的精力放在实现handler的功能上,并且handler最大的优势就是便于线程之间的通信,上面的例子我觉得如果按照实际开发可能并不是很恰当,相信在以后真真正正的使用到了HandlerThread,我对他的理解会更加清晰。

我对demo重新修改了一下,尽可能的让代码维护性提高,耦合性降低,需要的朋友可以下载。

Demo下载链接

这篇关于面试总结(1):HandlerThread的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1030583

相关文章

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

数据库面试必备之MySQL中的乐观锁与悲观锁

《数据库面试必备之MySQL中的乐观锁与悲观锁》:本文主要介绍数据库面试必备之MySQL中乐观锁与悲观锁的相关资料,乐观锁适用于读多写少的场景,通过版本号检查避免冲突,而悲观锁适用于写多读少且对数... 目录一、引言二、乐观锁(一)原理(二)应用场景(三)示例代码三、悲观锁(一)原理(二)应用场景(三)示例

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Python中连接不同数据库的方法总结

《Python中连接不同数据库的方法总结》在数据驱动的现代应用开发中,Python凭借其丰富的库和强大的生态系统,成为连接各种数据库的理想编程语言,下面我们就来看看如何使用Python实现连接常用的几... 目录一、连接mysql数据库二、连接PostgreSQL数据库三、连接SQLite数据库四、连接Mo

Git提交代码详细流程及问题总结

《Git提交代码详细流程及问题总结》:本文主要介绍Git的三大分区,分别是工作区、暂存区和版本库,并详细描述了提交、推送、拉取代码和合并分支的流程,文中通过代码介绍的非常详解,需要的朋友可以参考下... 目录1.git 三大分区2.Git提交、推送、拉取代码、合并分支详细流程3.问题总结4.git push