【朝花夕拾】Handler篇(二)
前言
一年前写过一篇文章【朝花夕拾】Handler篇,随着这一年来对Handler更多的认识和理解,本文对Handler知识点做的一些补充。
一、为什么要引入Handler
Handler的主要作用是切换线程,将线程切换到Handler所使用的Looper所在线程中去,我们大部分的开发者通常使用Handler是用于子线程通知主线程更新UI,我们需要明确的是更新UI只是Handler的其中一个作用而已。
那么为什么只能在主线程中更新UI,而不能在子线程中完成呢?因为Android系统规定,只能在主线程中访问UI,如果在子线程中访问UI,程序就会报错。在访问UI的时候,系统会调用ViewRootImpl类中的checkThread方法,如下所示:
//=======ViewRootImpl.java=======
final Thread mThread;
......
public ViewRootImpl(Context context, Display display) {
mThread = Thread.currentThread();
}
......
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
......
我们知道ViewRootImpl是View体系根View DecorView与Activity的PhoneWindow之间的纽带,最初ViewRootImpl实例化的时候,是在主线程中完成的。所以,上述checkThread方法可以用来判断当前是否为主线程,不是则报异常,该异常应该比较常见的了。
那么,系统为什么不允许在子线程中访问UI呢?这是因为UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。那么为什么系统不对UI控件的访问加上锁机制呢?主要是因为如果加上锁机制会有两个缺点:1)使访问逻辑变得复杂。2)降低访问UI的效率,因为锁机制会阻塞某些线程的执行。所以,最简单且高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只需要通过Handler切换一下UI访问的执行线程即可。
所以Handler的出现,就解决了子线程中不能访问UI的问题。
二、Handler回调所在线程问题
在对Handler理解不深入的时候,一直没有认真注意过new一个Handler后,回调方法所在的线程问题,总以为任何时候都可以在回调方法中更新UI。事实上,之所以会有这样的错误认识,是因为我们使用Handler的时候基本上都用于更新UI了,就犯了经验主义错误。实际上,回调方法所在线程,和发送消息的handler使用的Looper所在的线程有关。下面我们先通过一些实验开看看结果。
1、在子线程中使用main Looper的情况
private void testHandler() {
Log.i("songzheweiwang", "thread1=" + Thread.currentThread());
new Thread(new Runnable() {
@Override
public void run() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Log.i("songzheweiwang", "thread2=" + Thread.currentThread());
}
});
}
}).start();
}
在主线程中调用如上方法,对应的log如下,可见回调方法是在主线程中:
08-31 12:48:49.342 9414-9414/com.example.demos I/songzheweiwang: thread1=Thread[main,5,main] 08-31 12:48:49.373 9414-9414/com.example.demos I/songzheweiwang: thread2=Thread[main,5,main]
2、在子线程中使用子线程Looper的情况
private void testHandler() {
Log.i("songzheweiwang", "thread1=" + Thread.currentThread());
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
new Handler().post(new Runnable() {
@Override
public void run() {
Log.i("songzheweiwang", "thread2=" + Thread.currentThread());
}
});
Looper.loop();
}
}).start();
}
在主线程中调用该方法,得到的log如下,可见回调方法是在当前子线程中:
08-31 12:53:49.718 9655-9655/com.example.demos I/songzheweiwang: thread1=Thread[main,5,main] 08-31 12:53:49.719 9655-9750/com.example.demos I/songzheweiwang: thread2=Thread[Thread-7,5,main]
上述示例采用的是post方式,sendMessage方式也是一样的结果,这里就不举例了。我们平始使用Handler多数情况下是在主线程中new Handler的,默认情况下使用的是main Looper,然后在子线程中用该Handler实例来post或者sendMessage,所以默认情况下回调方法就是运行在主线程中,我们在该方法中访问UI就没有报错。
另外上述示例中,第6行到13行展示了在子线程中,Handler使用子线程Looper的使用方法。Looper.prepare(),是获取当前子线程的Looper,如果没有Looper会报异常。Looper.loop(),就是用来开启线程的消息循环,否则就无法收到消息。
【朝花夕拾】Handler篇(二)的更多相关文章
- 【基于WinForm+Access局域网共享数据库的项目总结】之篇二:WinForm开发扇形图统计和Excel数据导出
篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇二:基于OneNote难点突破和批量识别
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
- 《高性能javascript》 领悟随笔之-------DOM编程篇(二)
<高性能javascript> 领悟随笔之-------DOM编程篇二 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...
- SQL Server调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)
前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块 ...
- php基础篇-二维数组排序 array_multisort
原文:php基础篇-二维数组排序 array_multisort 对2维数组或者多维数组排序是常见的问题,在php中我们有个专门的多维数组排序函数,下面简单介绍下: array_multisort(a ...
- C蛮的全栈之路-node篇(二) 实战一:自动发博客
目录 C蛮的全栈之路-序章 技术栈选择与全栈工程师C蛮的全栈之路-node篇(一) 环境布置C蛮的全栈之路-node篇(二) 实战一:自动发博客 ---------------- 我是分割线 ---- ...
- 【SSRS】入门篇(二) -- 建立数据源
原文:[SSRS]入门篇(二) -- 建立数据源 通过 [SSRS]入门篇(一) -- 创建SSRS项目 这篇,我们建立了一个SSRS项目: 接下来,我们以 AdventureWorks2012 示例 ...
- 【转】java提高篇(二)-----理解java的三大特性之继承
[转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...
- CYQ.Data 轻量数据层之路 使用篇二曲 MAction 数据查询(十三)----002
原文链接:https://blog.csdn.net/cyq1162/article/details/53303390 前言说明: 本篇继续上一篇内容,本节介绍所有相关查询的使用. 主要内容提要: 1 ...
随机推荐
- 如何在Eclipse中查看Java类库的源代码
你的JDK安装目录下%Java_home%/src.zip文件就是源码,解压缩找到对应包下面的类即可. 如果是Eclipse开发,ctr+鼠标左击,出现不了源码的话,在弹出的视图中点击attach s ...
- C#使用CLR/C++的DLL间接调用Native C++的DLL
C#使用CLR/C++的DLL间接调用Native C++的DLL 开发环境:win 7 VS2010 简介:C#的exe使用CLR/C++间接调用Native C++的DLL. 第一步:创建一个C ...
- <<Modern CMake>> 翻译 2.2 CMake 编程
<<Modern CMake>> 翻译 2.2 CMake 编程 流程控制 CMake有一个 if 语句, 经年累月之后,现在它已经相当复杂. 您可以在 if 语句中使用全大写 ...
- 【Kali】kali linux的安装
资源准备 制作U盘启动盘 准备kali_Linux的分区 禁用快速启动 安装kali_linux 在这篇Blog中笔者将会讲解如何安装kali_linux,kali可以安装到虚拟机上,但这里笔者强烈建 ...
- 实战SpringCloud响应式微服务系列教程(第一章)
前言 在当今互联网飞速发展的时代,业务需求不断的更新和产品的迭代给系统开发过程和编程模式也带来巨大挑战,Spring Cloud微服务也随之应用而生,从springboot1.x到springboot ...
- rabbitMQ_rpc(六)
远程过程调用(RPC) 在前面我们已经学习了如何使用工作队列在多个消费者之间分配耗时的任务. 但是如果我们需要在远程计算机上运行功能并等待结果怎么办?那将会是一个不同的故事.此模式通常称为远程过程调用 ...
- 100天搞定机器学习|Day9-12 支持向量机
机器学习100天|Day1数据预处理 100天搞定机器学习|Day2简单线性回归分析 100天搞定机器学习|Day3多元线性回归 100天搞定机器学习|Day4-6 逻辑回归 100天搞定机器学习|D ...
- Could not link: /usr/local/etc/bash_completion.d/brew
用终端 brew update 或 brew install ** 时遇到的问题,详细如下: Error: Could not link: /usr/local/etc/bash_completion ...
- 10个常用的linux的命令
以下就是今天我们要介绍的Linux命令: man touch, cat and less sort and grep cut sed tar find diff uniq chmo ...
- java高并发系列 - 第21天:java中的CAS操作,java并发的基石
这是java高并发系列第21篇文章. 本文主要内容 从网站计数器实现中一步步引出CAS操作 介绍java中的CAS及CAS可能存在的问题 悲观锁和乐观锁的一些介绍及数据库乐观锁的一个常见示例 使用ja ...