Android中的Handler

<一> Handler的定义:

主要接受子线程发送的数据, 并用此数据配合主线程更新UI.

解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。  如果此时需要一个耗时的操作,例如: 联网读取数据,    或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,,会收到Android系统的一个错误提示  "强制关闭".  这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了.,来解决这个复杂的问题 ,    由于Handler运行在主线程中(UI线程中),  它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据)  , 把这些消息放入主线程队列中,配合主线程进行更新UI。(文/springhi-2009)

<二> Handler一些特点

handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),
        它有两个作用: (1):  安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行
      
        Handler中分发消息的一些方法
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)

以上post类方法允许你排列一个Runnable对象到主线程队列中,
        sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

<三> Handler实例

(1) 子类需要继承Hendler类,并重写handleMessage(Message msg) 方法, 用于接受线程数据

以下为一个实例,它实现的功能为 : 通过线程修改界面Button的内容

public class MyHandlerActivity extends Activity {

Button button;

MyHandler myHandler;

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.handlertest);

button = (Button) findViewById(R.id.button);

myHandler = new MyHandler();

// 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据

// Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象

// (2): 让一个动作,在不同的线程中执行.

// 它安排消息,用以下方法

// post(Runnable)

// postAtTime(Runnable,long)

// postDelayed(Runnable,long)

// sendEmptyMessage(int)

// sendMessage(Message);

// sendMessageAtTime(Message,long)

// sendMessageDelayed(Message,long)

// 以上方法以 post开头的允许你处理Runnable对象

//sendMessage()允许你处理Message对象(Message里可以包含数据,)

MyThread m = new MyThread();

new Thread(m).start();

}

/**

* 接受消息,处理消息 ,此Handler会与当前主线程一块运行

* */

class MyHandler extends Handler {

public MyHandler() {

}

public MyHandler(Looper L) {

super(L);

}

// 子类必须重写此方法,接受数据

@Override

public void handleMessage(Message msg) {

// TODO Auto-generated method stub

Log.d("MyHandler", "handleMessage......");

super.handleMessage(msg);

// 此处可以更新UI

Bundle b = msg.getData();

String color = b.getString("color");

MyHandlerActivity.this.button.append(color);

}

}

class MyThread implements Runnable {

public void run() {

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

Log.d("thread.......", "mThread........");

Message msg = new Message();

Bundle b = new Bundle();// 存放数据

b.putString("color", "我的");

msg.setData(b);

MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI

}

}

}

Android Handler Message多线程实例

什么是Handler

handler通俗一点讲就是用来在各个进程之间发送数据的处理对象。在任何进程中,只要获得了另一个进程的handler则可以通过 handler.sendMessage(message)方法向那个进程发送数据。基于这个机制,我们在处理多线程的时候可以新建一个thread,这 个thread拥有UI线程中的一个handler。当thread处理完一些耗时的操作后通过传递过来的handler像ui线程发送数据,由UI线程 去更新界面。

  Handler类简介

在Android平台中,新启动的线程是无法访问Activity里的Widget的,当然也不能将运行状态外送出来,这就需要有Handler机 制进行消息的传递了,Handler类位于android.os包下,主要的功能是完成Activity的Widget与应用程序中线程之间的交互。接下 来对该类中常用的方法进行介绍,如下表所示。

Handler类的常用方法

方法签名

描     述

public void handleMessage (Message msg)

子类对象通过该方法接收信息

public final boolean sendEmptyMessage (int what)

发送一个只含有 what 值的消息

public final boolean sendMessage (Message msg)

发送消息到 Handler ,

通过 handleMessage 方法接收

public final boolean hasMessages (int what)

监测消息队列中是否还

有 what 值的消息

public final boolean post (Runnable r)

将一个线程添加到消息队列

开发带有Handler类的程序步骤如下。

在Activity或Activity的Widget中开发Handler类的对象,并重写handleMessage方法。

在新启动的线程中调用sendEmptyMessage或者sendMe ssage方法向Handler发送消息。

Handler类的对象用handleMessage方法接收消息,然后根据消息的不同执行不同的操作。

在Android 中Handler和Message、Thread有着很密切的关系。Handler 主要是负责Message的分发和处理。但是这个Message从哪里来的呢?Message 由一个消息队列进行管理,而消息队列却由一个Looper进行管理。Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考Looper的源码。 可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的 Looper对象。Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消 息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该 线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。

参考:http://my.unix-center.net/~Simon_fu/?p=652

虽说 特定线程的消息只能分发给本线程,不能进行跨线程通讯,但是由于可以通过获得线程的Looper对象来进行曲线的实现不同线程间消息的传递,代码如下

使用Looper.myLooper静态方法可以取得当前线程的Looper对象。

使用mHandler = new EevntHandler(Looper.myLooper()); 可建立用来处理当前线程的Handler对象;其中,EevntHandler是Handler的子类。

使用mHandler = new EevntHandler(Looper.getMainLooper()); 可建立用来处理main线程的Handler对象;其中,EevntHandler是Handler的子类。

主线程发送消息:

在onClick的case 101中创建一个继承自Handler的EventHandler对象,然后获取一个消息,然后通过EventHandler对象调用 sendMessage把消息发送到主线程的MessageQueue中。主线程由系统创建,系统会给它建立一个Looper对象和 MessageQueue,所以可以接收消息。这里只要根据主线程的Looper对象初始化EventHandler对象,就可以通过 EventHandler对象发送消息到主线程的消息队列中。

主线程处理消息:

这里是通过 EventHandler的handleMessage函数处理的,其中收到的Message对象中what值为一的消息就是发送给它的,然后把消息里面附带的字符串在TextView上显示出来。

其他线程发送消息(这里是说不使用Runnable作为callback的消息):

首先bpostRunnable设为false,表示不通过Runnable方式进行消息相关的操作。然后启动线程noLooperThread, 然后以主线程的Looper对象为参数建立EventHandler的对象mNoLooperThreadHandler,然后获取一个Message并 把一个字符串赋值给它的一个成员obj,然后通过mNoLooperThreadHandler把消息发送到主线程的MessageQueue中。

主线程处理消息:

这里是通过 EventHandler的handleMessage函数处理的,其中收到的Message对象中what值为二的消息就是上面发送给它的,然后把消息里面附带的字符串在TextView上显示出来。

其他线程发送消息:

其他非主线程建立后没有自己的Looper对象,所以也没有MessageQueue,需要给非主线程发送消息时需要建立 MessageQueue以便接收消息。下面说明如何给自己建立MessageQueue和Looper对象。从OwnLooperThread的run 函数中可以看见有一个 Looper.prepare()调用,这个就是用来建立非主线程的MessageQueue和Looper对象的。

所以这里的发送消息过程是建立线程mOwnLooperThread,然后线程建立自己的Looper和MessageQueue对象,然后根据 上面建立的Looper对象建立对应的EventHandler对象mOwnLooperThreadHandler,然后由 mOwnLooperThreadHandler建立消息并且发送到自己的MessageQueue里面。

其他线程处理接收的消息:

线程要接收消息需要在run函数中调用Looper.loop(),然后loop函数会从MessageQueue中取出消息交给对应的 Handler对象 mOwnLooperThreadHandler处理,在mOwnLooperThreadHandler的handleMessage函数中会把 Message对象中what值为三的消息(上面发送的消息)在Log中打印出来,可以通过Logcat工具查看log。

其他线程发送消息(这里是说使用Runnable作为callback的消息):

首先 postRunnable设为true,表示通过Runnable方式进行消息相关的操作。然后启动线程noLooerThread, 然后以主线程的Looper对象为参数建立EventHandler的对象mNoLooperThreadHandler,然后获取一个Message并 把一个字符串赋值给它的一个成员obj,然后通过mNoLooperThreadHandler把消息发送到主线程的MessageQueue中。 

主线程处理消息:

主线程收到上面发送的Message后直接运行上面Runnable对象中的run函数进行相应的操作。run函数通过Log打印一个字符串,可以通过Logcat工具查看 log。

主线程发送消息:

这里首先要求线程receiveMessageThread运行(在onCreate函数中完成),并且准备好自己的 Looper和MessageQueue(这个通过ReceiveMessageThread中的run函数中的Looper.prepare()调用完 成),然后根据建立的Looper对象初始化Handler对象mOtherThreadHandler。然后在onClick的case 105中由mOtherThreadHandler建立一个消息(消息中有一个字符串对象)并且发送到线程receiveMessageThread中的 MessageQueue中。

其他线程处理接收的消息:

线程要接收消息需要在run函数中调用Looper.loop(),然后loop函数会从MessageQueue中取出消息交给对应的 Handler对象mOtherThreadHandler处理,在mOtherThreadHandler的handleMessage函数中会把 Message对象中的字符串对象在Log中打印出来,可以通过Logcat工具查看log。

Android中的MessageQueue,Handler,Looper和Thread

文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli@gmail.com>

前几天和一位同事讨论Android中Handler和Thread,其中一个问题是:创建Handler时会不会创建Thread?

我对JAVA编程不熟,但直觉告诉我不会:我认为Handler只是用来辅助实现异步操作的东西。当时我拿了GTK+中的idle来做对比,sendMessage就相当于加一个idle函数,系统处理完前面的Message后就会处理这个Message。毕竟没有看过里面的实现代码,所以当时并不确信。今天看了下:

MessageQueue

消息队列MessageQueue是一个以执行时间为序的优先级队列:

o 普通消息的执行为当前时间,先发送的前面,后发送在后面,这是典型的FIFO。

o 最高优先级的消息执行时间为0,所以直接插在队列的最前面,通常会立即执行。

o 而在将来执行的Message相当于timer,执行时间为当前时间+delay的时间。

MessageQueue的函数boolean enqueueMessage(Message msg, long when)用来向队列中插入消息。

Message和GTK+ idle不同之处在于,Message只是一段数据,里面说明了要做什么,但不并知道如何做。而idle带有自己的数据和处理函数,它知道如何做。Message放入队列中后,在处理这些消息时到底要做些什么呢?这就引入了Handler:

Handler

Handler 对消息队列的enqueueMessage做了包装,这其实并不重要,因为完全可以直接调用enqueueMessage来实现。重要的Handler把在包装enqueueMessage的同时,把Message的target设成了自己,即为Message指定执行的行为:

        if (queue != null) {msg.target = this;sent = queue.enqueueMessage(msg, uptimeMillis);}

这样一来,当前Message被处理的时候就会调用Handler的dispatchMessage,而这个函数就会调用你要实现的虚函数handleMessage了。经过消息队列转了一圈,还是调用了你自己的实现函数,但是同步操作变成了异步操作。

    public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

Looper

Message放在消息队列里了,谁来一个一个的取出来处理呢?这时轮到Looper上场了,它的函数loop会一直循环处理队列中的消息,直到遇上一个没有target的Message:

    public static final void loop() {Looper me = myLooper();MessageQueue queue = me.mQueue;while (true) {Message msg = queue.next(); // might block//if (!me.mRun) {//    break;//}if (msg != null) {if (msg.target == null) {// No target is a magic identifier for the quit message.return;}if (me.mLogging!= null) me.mLogging.println(">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);msg.target.dispatchMessage(msg);if (me.mLogging!= null) me.mLogging.println("<<<<< Finished to    " + msg.target + " "+ msg.callback);msg.recycle();}}}

由此可见:一个Looper总是和一个MessageQueue关联起来的。

Thread

loop只是一个函数,它也需要别人来执行它。由于它一执行就会阻塞在那里,所以一定需要一个线程来调用:

  *  class LooperThread extends Thread {*      public Handler mHandler;*      *      public void run() {*          Looper.prepare();*          *          mHandler = new Handler() {*              public void handleMessage(Message msg) {*                  // process incoming messages here*              }*          };*          *          Looper.loop();*      }*  }

一个Looper也总是和一个Thread关联起来的,不过不一定要创建新线程,可以是主线程的。Handler和Thread不是一一对应的,理论上,在一个LooperThread中,可以有任何多个Handler,每个消息都可以指定不同的Handler,因为每个消息都可以有不同的行为。

在创建Handler时并不会创建Thread,它只是取当前线程的Looper的MessageQueue:

    public Handler() {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = null;}

通过myLooper取出当前线程的Looper:

    public static final Looper myLooper() {return (Looper)sThreadLocal.get();}

这个Looper是在Looper.prepare里创建的:

    public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}

Thread和Looper以及Handler和Message详解 Android开发必读

很多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,今天Android123就这一问题做更系统性的总结。

 Android开发过程中为什么要线程能?

我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。

Android中使用Thread线程会遇到哪些问题?

对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。

1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒,如下:

2. 当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。

3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,昨天的问题 Can't create handler inside thread 错误 一文中提到这一概念,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。

4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的    sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 :               
               

5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,我们这里Android开发网对其不做过多的赘述,相关原因参考下面的介绍:

6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务,具体的Android123在以前的文章中有详细解释,可以使用站内搜索来了解更多。

转载于:https://www.cnblogs.com/qingblog/archive/2012/07/18/2597834.html

Android Handler讲述很详细的一篇文章相关推荐

  1. android Handler机制原理解析(一篇就够,包你形象而深刻)

    首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员(属于某个快递公司的职员) Message:包裹(可以放置很多东西的箱子) MessageQueue:快递分拣中心( ...

  2. 注意力机制的基本思想和实现原理(很详细)(第二篇)

    接上一篇:https://blog.csdn.net/hpulfc/article/details/80448570 作者:张俊林 链接:https://www.zhihu.com/question/ ...

  3. Android Animation动画(很详细)

    Animations 一.Animations介绍 Animations是一个实现android UI界面动画效果的API,Animations提供了一系列的动画效果,可以进行旋转.缩放.淡入淡出等, ...

  4. android 界面布局 很好的一篇总结 【转】

    布局:   在 android 中我们常用的布局方式有这么几种: 1.LinearLayout ( 线性布局 ) :(里面只可以有一个控件,并且不能设计这个控件的位置,控件会放到左上角) 线性布局分为 ...

  5. 小米八android耗电比例很大,手机技巧 篇一:小米新系统推送,耗电严重待解决!教你5招实现三天一充...

    手机技巧 篇一:小米新系统推送,耗电严重待解决!教你5招实现三天一充 2019-04-29 18:36:26 0点赞 1收藏 0评论 MIUI新系统推送不知道有没有接受的到呢?表示楼主已经接收到更新推 ...

  6. 阿里P8大佬亲自教你!Android内存泄漏总结,看看这篇文章吧!

    前言 这次去腾讯面试的是我大学同学,我们大学都是一学习,一起吃饭,一起洗脚,一起... 他们公司最近也裁员了,不过他是裁员前去的腾讯,不知道谁捞到他简历了,莫名就走了流程,他莫名的就面了6轮过了. 他 ...

  7. 【转】对新手很不错的一篇文章,没事可以看一下

    转载自:http://www.xici.net/main.asp?url=/u11048681/d42329771.htm 从理论上说,任何一门语言都可以在任何一个系统上编程,只要找到该系统提供的&q ...

  8. 考研英语很重要,这篇文章教你如何通过它

    对于我来说,考研路上最大的阻碍不是专业课,而是考研英语.不知道你们有没有同感,就是从小到大,尽管学了这么些年的英语,但还是该会的还是不会. 就真的很难受. 但是为了考研,再难也还是要学的啊!TAT 所 ...

  9. Java 注解 --很有趣的一篇文章

    初学者可以看看,很有趣的文章,简单易懂,生动有趣. 原文链接:https://blog.csdn.net/briblue/article/details/73824058

最新文章

  1. 科讯CMS--用SQL标签时提示内存不够
  2. 记录node内存瓶颈分析
  3. MySQL使用二进制日志恢复数据库
  4. 清华王兴再抛神论:为什么教育决定着中国餐饮业质量?
  5. 合并两个有序链表,合并后依然有序(C语言)
  6. C++中实现类似php的UTF8和UrlEncode函数的代码
  7. MVC4 数据验证、特性、自动属性总结
  8. SpringBoot系列(3):SpringBoot2.1.x源码阅读环境搭建详解
  9. 恒生电子笔试题数据库及算法整理记录
  10. matlab控制读取数小数位,matlab输出的数值型矩阵中如何控制小数位数,以及对齐方式?...
  11. 测试理论基础篇~有它一篇就够了!
  12. C语言字节对齐规则总结
  13. java socket 读取文件_java中ServerSocket读取文件流不是分行读取
  14. Ubuntu20.04+Nvidia RTX 3060 显卡驱动安装
  15. 使用低功耗蓝牙技术将可穿戴无线传感器连接到物联网---凯利讯半导体
  16. 安卓029老年人监护系统APP
  17. php开发gui有啥意义,gui是什么?
  18. 华硕java安装教程win10_华硕骁龙835笔记本安装win10系统操作教程
  19. aspose无法在Linux无法运行,com.aspose 生成pdf在linux生成PDF乱码解决(window环境正常)...
  20. Java工具类 计算某个时间距离当前时间相差多少天、多少小时、多少分、多少秒

热门文章

  1. 搭建网站用虚拟主机好还是云服务器好?
  2. 对当前医院奖金分配制度的探讨
  3. gbase 8d客户端功能事项
  4. MapStruct Unknown property “xx“ in result type XXX. Did you mean “null“?
  5. 订阅切换按钮(subscribe toggle button)
  6. VBA清除除第一行之外所有表格的数据
  7. java计算机毕业设计老鹳窝旅游网源码+系统+数据库+lw文档+mybatis+运行部署
  8. Transformer——patch embedding代码
  9. [论文笔记]Maiter:一种基于Delta的累积迭代计算的异步图处理框架
  10. 【Paper】2017_Consensus of linear multi-agent systems with exogenous disturbance generated from hetero