如何使用Handler
什么是Handler?
Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象。
handler类有两种主要用途:
- 执行Runnable对象,还可以设置延迟。
- 两个线程之间发送消息,主要用来给主线程发送消息更新UI。
为什么要用Handler
解决多线程并发问题,假设如果在一个activity中,有多个线程去更新ui,并且都没有加锁机制,那界面显示肯定会不正常。于是andoird官方就封装了一套更新ui的机制,也可以用handler来实现多个线程之间的消息发送。
如何使用Handler
handler常用的方法有以下这些:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable,long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
我们可以看到这些方法主要分为两类,一种是传入一个Runnable对象,一种是传入一个Message对象。
用代码来学习post一个Runnable对象
先创建Handler对象,直接new一个就行
private Handler handler=new Handler();
实现Runnable接口,用匿名实现方式,重写run方法,就打印一个字符串。
private Runnable runnable=new Runnable() {
@Override
public void run() {
Log.i("MainActivity","Handler Runnable");
}
};
然后我们调用handler的post方法,这里需要注意的是,post一个Runnable对象,底层用的是回调,不会开启一个新的线程,所有Runnable的run方法还是在主线程里面。是可以更新UI的。
handler.post(runnable);//执行
handler.postDelayed(runnable,2000);//延迟2秒后执行
运行程序,控制台打印的log如下:
05-18 19:17:14.901 17750-17750/com.ansen.handler I/MainActivity: Handler Runnable
05-18 19:17:16.901 17750-17750/com.ansen.handler I/MainActivity: Handler Runnable
从上面的log我们可以看到两条Log的时间相差两秒。这是因为我们用postDelayed方法的时候第二个参数设置了两秒的延迟。
使用sendMessage方法传递消息
从方法的名字上我们可以理解用来发送消息,这个方法在android中使用频率比较高,因为在Android中多线程中是不能更新UI的,必须要通过Handler把消息传递给UI线程,才能更新UI。当然也可以用Handler来两个子线程发送消息。
我们给activity_main文件中TextView控件设置一个id,然后在MainActivity中查找这个控件,在多线程的for循环中给TextView赋值。增加后的代码如下:
textview= (TextView) findViewById(R.id.textview);
new Thread(new Runnable(){
@Override
public void run(){
for(int i=1;i<=100;i++){
Log.i("MainActivity","当前值是:"+i);
textview.setText("当前值是:"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
重新运行代码,程序奔溃。控制台打印如下log:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6024)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:820)
这是因为在android中不能在多线程中更新UI造成的。
每个应用启动的时候,Android会启动一个对应的主线程用来处理UI相关的事情,例如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理,所以主线程通常又被叫做UI线程。
这个时候我们就会用到Android的Handle类,Handle可以帮我们解决多线程不能更新UI问题,这里我们只要知道使用这个类就行,在后面我们会详细介绍它的原理。
接下来我们看如何用handler在主线程中接受子线程的消息,并且更新UI。首先new一个Handler的时候实现他的handleMessage方法,修改后的代码如下:
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==UPDATE_UI){
textview.setText("当前值是:"+msg.obj);
}
}
};
我们可以看到把更新TextView的代码放到这里来了,并且用到handleMessage的msg参数。这个对象我们常用的一般就两个属性,what就是一个标示,我们发送消息的时候必需要指定值。obj:发送消息的参数。
再来看看多线程的run方法做了哪些改动,首先调用obtainMessage方法,这个方法呢是从消息池里面返回一个Message对象,如果消息池没有才会创建对象,这样避免一直去new Message对象。message对象有what属性是必需要赋值的,是一个int类型。前面我们讲到过了,是一个标示。obj是发送消息用来传参,这里我们传入的是i的值。最后调用handler.sendMessage(message)方法。然后我们handler的handleMessage方法就会回调。
new Thread(new Runnable(){
@Override
public void run(){
for(int i=1;i<=100;i++){
Log.i("MainActivity","当前值是:"+i);
Message message=handler.obtainMessage();
message.what=UPDATE_UI;
message.obj=i;
handler.sendMessage(message);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
还有sendEmptyMessage跟sendMessageDelayed方法我就不一一给大家解释了,有兴趣的朋友自己去实现一下。
源码下载
如果你想第一时间看我的后期文章,扫码关注公众号,每周不定期推送Android开发实战教程文章...
Android开发666 - 安卓开发技术分享
扫描二维码加关注
如何使用Handler的更多相关文章
- android Handler介绍
Handler使用介绍: Handler根据接收的消息,处理UI更新.Thread线程发出消息,通知Handler更新UI. Handler mHandler = new Handler() { p ...
- Handler
1.1 继承AbstractController优点:能定制请求方式 package cn.happyl.controller; import javax.servlet.http.HttpServl ...
- Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...
- Android笔记——Handler Runnable与Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
- Android消息传递之Handler消息机制
前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...
- Handler系列之内存泄漏
本篇简单的讲一下平常使用Handler时造成内存泄漏的问题. 什么是内存泄漏?大白话讲就是分配出去的内存,回收不回来.严重会导致内存不足OOM.下面来看一下造成内存泄漏的代码: public clas ...
- Handler系列之创建子线程Handler
上一篇我介绍了Handler机制的工作原理,默认情况下,ActivityThread类为我们创建的了主线程的Looper和消息队列,所以当你创建Handler之后发送消息的时候,消息的轮训和handl ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- Handler系列之使用
作为一个Android开发者,我们肯定熟悉并使用过Handler机制.最常用的使用场景是"在子线程更新ui",实际上我们知道上面的说话是错误的.因为Android中只有主线程才能更 ...
- 阶段一:用Handler和Message实现计时效果及其中一些疑问
“阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 本来是打算继续做天气预报的优化的,但因为某些原因,我要先把之前做的小应用优化一下.所以今天就插播一下用Handle ...
随机推荐
- 函数调用过程&生成器解释
摘自马哥解答,感谢. 函数调用过程: 假设程序是单进程,单执行流,在某一时刻,能运行的程序流只能有一个.但函数调用会打开新的执行上下文,因此,为了确保main函数可以恢复现场,在main函数调用其它函 ...
- 回答: 2017-03-19的关于css+div布局的疑问 2017-04-05
原问题为红色,回答为黑色 第一次布局一个静态网页,发现许多细节都需要自己探索,出现诸如以下问题: 1.布局问题:经常出现一个div被挤出来?做到一半少一个div布局? 布局之前,要点打好框架,明确每个 ...
- DC平衡双向控制串行器 转接IC GM8913:LVTTL转FPD-LINK III芯片
1 概述 GM8913型DC平衡双向控制串行器,其主要功能是实现将10或12位并行控制信号和一路时钟信号串行为一路2.8Gbps高速串行数据:同时接收低速通道信号实现模式配对的功能.芯片内部集 ...
- require.js学习笔记
使用require.js的好处? 1 有效的防止命名冲突(可以将变量封装在模块内,通过暴露出的接口解决命名冲突) 2 解决不同JS文件中的依赖 3 可以让我们的代码以模块化的方式组织 官方网站http ...
- Windows文件路径转换为java中可识别的文件路径的转义方法,(另附转义多种格式)
ps:欢迎加qq好友:2318645572,交流学习 一:路径转化 Windows中的文件路径格式为 D:\eclipse\apache-tomcat-7.0.67\wtpwebapps\... Ja ...
- 基础SELECT示例掌握
SELECT查询语句 ---进行单条记录.多条记录.单表.多表.子查询-- SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [MAX_ST ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- PHP原生DOM对象操作XML'代码'
对于操作XML类型文件,PHP内置有一套DOM对象可以进行处理.对XML的操作,从创建.添加到修改.删除都可以使用DOM对象中的函数来进行. 创建 创建一个新的XML文件,并且写入一些数据到这个XML ...
- 【原创】bootstrap框架的学习 第六课[bootstrap代码]
Bootstrap 允许您以两种方式显示代码: 第一种是 <code> 标签.如果您想要内联显示代码,那么您应该使用 <code> 标签. 第二种是 <pre> 标 ...
- file_get_contents url
file_get_contents (PHP 4 >= 4.3.0, PHP 5) file_get_contents — 将整个文件读入一个字符串 说明¶ string file_get_co ...