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的更多相关文章

  1. Android 消息机制 (Handler、Message、Looper)

    综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...

  2. Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android消息机制探索(Handler,Looper,Message,MessageQueue)

    概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...

  5. android 消息机制,handler机制,messageQueue,looper

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha handler  就是 处理器 .  用来处理消息, 发送消息. handler   就 ...

  6. Android消息机制:Looper,MessageQueue,Message与handler

    Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...

  7. 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 进程间 通讯 ...

  8. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)

    不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...

  9. Android进阶——Android消息机制之Looper、Handler、MessageQueen

    Android消息机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 在安卓开发中,常常会遇到获取数据后更新UI ...

随机推荐

  1. poj1651 最优矩阵乘法动态规划解题

    题目描述: 有若干个矩阵{Ai},元素都为整数且已知矩阵大小. 如果要计算所有矩阵的乘积A1 * A2 * A3 .. Am,最少要多少次整数乘法? 输入 第一行一个整数n(n <= 100), ...

  2. poj1657---chessboard

    对棋盘横纵坐标的解读 str1="f3" str2="e9" x=abs(str1[0]-str2[0]) y=abs(str1[1]-str1[1]) 如果x ...

  3. 简单实现计算Edit Distance算法

    最近因为工作需要,学习了NLP的相关知识,简单动手实现了一下计算Edit Distance的算法,就是计算一个字符串要变成另一个字符串需要的代价,这其中采用Levenshtein方式,即规定一个插入和 ...

  4. ObjectiveC中的block用法解析

    Block Apple 在C, Objective-C,C++加上Block这个延申用法.目前只有Mac 10.6 和iOS 4有支持.Block是由一堆可执行的程序组成,也可以称做没有名字的Func ...

  5. JVM参数

    下面给出各种可以用户设置堆大小的命令行参数.注释列提供了一些设置参数的初级或高级建议.此外,后面还会给出更多的详细建议. 参数 描述 注释 -Xms<size> 设置堆的最小值 在生产阶段 ...

  6. HTML之学习笔记(四)格式化标签和特殊字符

    html常用的格式化标签使用如下 <html> <head> <title></title> </head> <body > & ...

  7. Android学习笔记_点九绘图与软键盘和事件传递

    最近项目里遇到的几个小问题,以前只是用吗没有深入看过,现在总结到一起,防止以后这种小问题占用太多时间.还是通过网上别人总结的很多博客学习了,挑选出最易懂明了的. 还有leader很小的问题都不放过,亲 ...

  8. MS SQL:ID自增列从1开始重新排序

    数据库中把ID自增长重置成1: 一般做法:(太麻烦) 复制表数据->删除原表.新建一张表->粘贴: 新方法: 数据库中:新建查询->复制.粘贴一下代码->修改表名,执行即可(先 ...

  9. A - 数塔

    A - 数塔 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  10. Cglib学习站点(转)

    1.CGlib简单介绍,访问地址:http://www.blogjava.net/stone2083/archive/2008/03/16 /186615.html,从简单的示例到不同业务场景的变化, ...