Android消息机制之Handler
Android为什么要提供Handler
Android建议我们不要在UI线程中执行耗时操作,因为这很容易导致ANR异常(在Android源码中我们可以看到,UI如果对用户的操作超过5秒无响应,就会报ANR异常)。因此,一些耗时操作都会在子线程中完成。当我们在子线程中获取了数据,要将其显示到UI中,如果没有Handler,这将很难完成。因此,Android之所以提供Handler,就是为了解决子线程访问UI的问题。
为什么Android不允许在子线程中访问UI呢?显然这样做不安全,多线程访问UI是不安全的(学过操作系统的盆友应该都了解线程互斥,这里我就不详细介绍了)。有人就会说了,可以通过设置信号量来解决啊。这中方法不是不可以,因为这种方法会使访问UI的逻辑变得复杂;其次这会降低UI的访问效率。而使用Handler就比较简单高效。Handler是同个Message来通讯的。
Handler的用法
使用Handler时,需要重写handleMessage方法,在handleMessage中接受新线程发来的Message,并做相应的处理。在新线程中则是通过Message来传递消息,Message中往往也携带着需要传递的数据以及消息的类型。还要强调一点,如果当前线程有Looper就不需要执行Looper.prepare(),如果没有,就需要在新线程内执行Looper.prepare(),否则会报错。具体使用代码如下:
- public class MainActivity extends AppCompatActivity {
- private Handler mHandler=new Handler(){
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what)
- {
- case 1:
- //执行需要修改的UI操作
- break;
- default:
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- new Thread(new Runnable() {
- @Override
- public void run() {//在新线程中执行耗时操作
- //如果当前线程有Looper就不需要执行Looper.prepare();
- Looper.prepare();
- try {
- Thread.sleep(1000);//睡眠1秒
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //操作完成之后通过发送Message,来通知Handler进行UI操作
- Message msg=new Message();
- msg.what=1;
- /*这部分是伪代码,value 是想通过Message传递的值
- Bundle data=new Bundle();
- data.putSerializable("key",value);
- msg.setData(data);
- */
- //设置好数据后,发送消息
- mHandler.sendMessage(msg);
- }
- }).start();
- }
- }
当然,handler也可以在子线程中创建,代码如下:
- private TextView tv_test;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.handler_test_layout);
- tv_test= (TextView) findViewById(R.id.tv_test);
- }
- //button点击的函数
- public void click(View v)
- {
- new Thread(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- Handler handler=new Handler(Looper.getMainLooper()){
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what)
- {
- case 1:
- tv_test.setText("receive msg");
- }
- }
- };
- Message msg=new Message();
- msg.what=1;
- handler.sendMessage(msg);
- }
- }).start();
- }
上面的代码是,当点击按钮后,就会创建一个新的线程,在新线程中创建handler,并发送消息、接受消息。这里需要注意的是,在新线程中创建handler需要使用Handler handler=new Handler(Looper.getMainLooper())这样的写法,Looper.getMainLooper()将主线程中的Looper传过去,这样handler才能访问UI。运行效果如下:
Handler的内部机制
Handler创建时会采用Looper来建立消息循环。所以,当前线程必须要有Looper。当Handler创建完成后,其内部的Looper以及MessageQueue既可以和Handler一起协同工作了。Handler通过sendMessage将消息发送给内部的MessageQueue,而MessageQueue会调用queue.enqueueMessage(msg, uptimeMillis)方法,它的源码如下:
- boolean enqueueMessage(Message msg, long when) {
- if (msg.target == null) {
- throw new IllegalArgumentException("Message must have a target.");
- }
- if (msg.isInUse()) {
- throw new IllegalStateException(msg + " This message is already in use.");
- }
- synchronized (this) {
- if (mQuitting) {
- IllegalStateException e = new IllegalStateException(
- msg.target + " sending message to a Handler on a dead thread");
- Log.w(TAG, e.getMessage(), e);
- msg.recycle();
- return false;
- }
- msg.markInUse();
- msg.when = when;
- Message p = mMessages;
- boolean needWake;
- if (p == null || when == 0 || when < p.when) {
- // New head, wake up the event queue if blocked.
- msg.next = p;
- mMessages = msg;
- needWake = mBlocked;
- } else {
- // Inserted within the middle of the queue. Usually we don't have to wake
- // up the event queue unless there is a barrier at the head of the queue
- // and the message is the earliest asynchronous message in the queue.
- needWake = mBlocked && p.target == null && msg.isAsynchronous();
- Message prev;
- for (;;) {
- prev = p;
- p = p.next;
- if (p == null || when < p.when) {
- break;
- }
- if (needWake && p.isAsynchronous()) {
- needWake = false;
- }
- }
- msg.next = p; // invariant: p == prev.next
- prev.next = msg;
- }
- // We can assume mPtr != 0 because mQuitting is false.
- if (needWake) {
- nativeWake(mPtr);
- }
- }
- return true;
- }
通过源码,我们发现,queue.enqueueMessage(msg, uptimeMillis)将消息放入了MessageQueue里。Looper则会一直处理MessageQueue中的消息。
Android消息机制之Handler的更多相关文章
- Android 消息机制 (Handler、Message、Looper)
综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...
- Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- Android消息机制探索(Handler,Looper,Message,MessageQueue)
概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...
- android 消息机制,handler机制,messageQueue,looper
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha handler 就是 处理器 . 用来处理消息, 发送消息. handler 就 ...
- Android消息机制:Looper,MessageQueue,Message与handler
Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...
- Android进阶——Android消息机制之Looper、Handler、MessageQueen
Android消息机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 在安卓开发中,常常会遇到获取数据后更新UI ...
随机推荐
- SQL Server Service Borker 1
1.消息类型定义: 消息类型,是信息交换的模板.create message type message_type_name validattion = well_formed_xml; 2.约定定义: ...
- 解决Oracle 11gR2 空闲连接过多,导致连接数满的问题
今天又遇到了11gR2连接数满的问题,以前也遇到过,因为应用那边没有深入检查,没有找到具体原因,暂且认为是这个版本Oracle的BUG吧. 上次的处理办法是用Shell脚本定时在系统中kill v$ ...
- 优盘(U 盘) 采用TLC, MLC, SLC芯片 的区别 与使用寿命
最近一直在看大家在讨论sandisk,pny,金士顿等大厂都开始用tlc的芯片问题,让大家基本都不敢用U盘存数据了按照之前的擦写参数TLC 1000次MLC 10000次SL ...
- Unix/Linux环境C编程入门教程(3) Oracle Linux 环境搭建
Unix/Linux版本众多,我们推荐Unix/Linux初学者选用几款典型的Unix/Linux操作系统进行学习. 2010年9月,Oracle Enterprise Linux发布新版内核--Un ...
- CSliderCtrl鼠标点击精确定位
实现CSliderCtrl的子类CXXCtrl 响应左键按下消息 ON_WM_LBUTTONDOWN() void CXXCtrl::OnLButtonDown(UINT nFlags, CPoint ...
- display的table和cell外加table-layout:fixed等分布局,外加换行,word-wrap:break-word
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HDU 4937 Lucky Number 规律题_(:зゝ∠)_
把全部合法的进制打出来会发现合法的进制都是在 n/3 n/4 n/5的边上 然后暴力边上的进制数.. #include <cstdio> #include <set> type ...
- xcode -饼状进度条
界面搭建 创建一个画饼状的类 eatView 集成UIView #import "eatView.h" @implementation eatView // Only overr ...
- sql获取表字段名、描述和类型
SELECT TableName = OBJECT_NAME(c.object_id), ColumnsName = c.name, Description = ex.value, ColumnTyp ...
- <转>ASP.NET学习笔记之理解MVC底层运行机制
ASP.NET MVC架构与实战系列之一:理解MVC底层运行机制 今天,我将开启一个崭新的话题:ASP.NET MVC框架的探讨.首先,我们回顾一下ASP.NET Web Form技术与ASP.NET ...