版权声明:本文为博主原创文章,未经博主同意不得转载。

https://blog.csdn.net/u012974916/article/details/24580405

一  Handler作用和概念 

    包括线程队列和消息队列。实现异步的消息处理机制。跟web开发的ajax有异曲同工之妙。

 

      1.运行在某个线程上,共享线程的消息队列。 
      2.接收消息、调度消息,派发消息和处理消息; 
      3.实现消息的异步处理;

        Handler能够让你发送和处理消息,以及Runnable对象。每一个Handler对象相应一个Thread和Thread的消息队列。

当你创建一个Handler时,它就和Thread的消息队列绑定在一起,然后就能够传递消息和runnable对象到消息队列中,运行消息后就从消息队列中退出。

  Handler的作用就是:调度消息和runnable对象去被运行;使动作在不同的线程中被运行。

  当一个应用程序中进程被创建时。它的主线程专门运行消息队列(messageQueue),去管理顶层的应用程序相关的对象如:activity,broadcastReceiver。windows等。你能够创建你的Thread,和主线程进行交互——通过Handler,交互的方法就是通过post或者sendMessage。可是在你的新线程中。给定的Message或者Runnable,会在适当的时候的被调度和处理。

(即不会被马上处理——堵塞式)。

实际上就是建立消息处理模型/系统

   要学习Handler,看到肯定是和消息有关,可能还是须要先熟悉一下消息系统的构成和简单原理。

以下就先学习一下消息系统的基本原理。

二 消息系统的基本原理和构成

       从一般的消息系统模型的建立大致构成以下几个部分:

    1. 消息原型

    2. 消息队列

    3. 发送消息

    4.消息循环

    5.消息获取

    6.消息派发

    7.消息处理

大致模型图例如以下:

 

    

 消息系统模型通常会包括以上七个部分(消息原型,消息队列,消息发送,消息循环。消息获取,

消息派发。消息处理)。实际上的核心是消息队列和消息循环,其余部分都是环绕这两部分进行的。

  从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息

系统模型相比。那么Handler又是怎样囊括这七个部分的呢?

  在Android中对这六个部分进行了抽象成四个独立的部分(消息循环):

    Handler,Message,MessageQueue,Looper。

  •   Message就是消息原型。包括消息描写叙述和数据。
  •   MessageQueue就是消息队列。
  •   Looper完毕消息循环
  •   Handler就是驾驭整个消息系统模型。统领Message,MessgeQueue和Looper;

  Handler能够实现消息系统模型,那么详细是怎样进行工作的呢,以下探究一下这当中工作的方法和原理。

三 Handler工作原理分析

  要了解Handler工作原理,先看一下这个系统模型详细组成的层次结构框架是个什么样的。

      

1.Looper:

  实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;

须要主动去创建。然后启动Looper的消息循环loop;与外部的交互通过Handler进行;

2.MessageQueue:

  消息队列,由Looper所持有,可是消息的增加是通过Handler进行;

  消息循环和消息队列都是属于Thread。而Handler本身并不具有Looper和MessageQueue;

可是消息系统的建立和交互。是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。所以消息系统模型的核心就是Looper。

消息循环和消息队列都是由Looper建立的。

而建立Handler的关键就是这个Looper。

  一个Thread同一时候能够相应多个Handler,一个Handler同一时候仅仅能属于一个Thread。Handler属于哪个

Thread取决于Handler在那个Thread中建立。

  在一个Thread中Looper也是唯一的。一个Thread相应一个Looper,建立Handler的Looper来自哪个Thread。

Handler属于哪个Thread。

  故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完毕消息的异步处理。

Handler与Thread及Looper的关系能够用以下图来表示:

    

 

       Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,用来实现Thread消息系统模型,依附于Thread上。

在线程建立Handler时:

  使Handler满足消息系统须要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。

在线程中建立Handler,须要做以下工作:

  1.  获取Thread中的Looper交给Handler的成员变量引用维护;

  2.  通过Looper获取MessageQueue交给Handler的成员变量引用维护。

  那么消息系统模型建立完毕之后,依照消息系统运行。从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完毕。

消息发送和派发接口:

  1.  post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数运行。

  2.  sendMessage(Message)消息。经过消息循环派发消息处理函数中处理消息;

  3.  dispatchMessage       派发消息,若是post或带有回调函数则运行回调函数,否则运行

      消息处理函数Handler的handleMessage(通常派生类重写)。

 以上就是Handler怎样实现Thread消息系统模型的大致介绍。

以下将详细分析是怎样实现消息系统模型运行的。

四 Handler实现流程分析

  我们知道Handler就是一个消息系统的外壳。属于某个Thread并包装了Thread的Looper及其MessageQueue。与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,消息系统模型的核心是Looper。

  以下看看Handler是怎样建立跑起来的。以msg消息为例,runnable实质是一样。

1 Handler的建立

  Handler唯一属于某个Thread,在某个Thread中建立Handler时。须要获取Thread的Looper

及其MessageQueue,建立Handler关键是Looper的来源。

  Handler提供了好几个构造函数但其本质一致:由外部传入Looper:当前线程或其它线程 
  

  public Handler(Looper looper) {
        //初始化构建消息系统參数
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
  }

从当前线程获取:由创建Handler的Thread决定

       

  public Handler() {
        //初始化构建消息系统參数
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
  }   public static Looper myLooper() {
return sThreadLocal.get();
}

 无论哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。要实现消息循环必须确保Thread的Looper建立。怎样确保呢?

Looper提供了静态函数:

public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
} sThreadLocal.set(new Looper());
} //存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

看到这里刚開始让我非常是奇怪和迷惑:

     Looper一个独立的类。又不属于某个Thread,而这里创建Looper的函数又是静态的,

属于整个Looper类;创建Looper之后交给静态成员变量sThreadLocal保存,获取

sThreadLocal.get()。那么一个静态变量属于整个类,属性更改始终有效。一次创建之后

sThreadLocal.get()永远都不等于null!

  而Thread和Looper是唯一相应的。那这里岂不是全部的Thread都是用同一个Looper,不可能!所以肯定这个ThreadLocal是有玄机的。网上一查:

  ThreadLocal:

    维护线程的变量,为每一个使用该变量的线程实例提供独立的变量副本。每一个线程都能够独立使用该变量,而互不影响。

(详细可參考:http://blog.csdn.net/qjyong/article/details/2158097

  所以每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。要建立Handler,须要先创建线程的Looper,才干建立消息系统模型。

通过Looper我们建立了Thread上的消息系统模型Handler,能够来进行消息系统的一系列流程了。

2 消息发送

消息发送两种方式:post和sendMessage。

       post:针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)

       sendMessage:针对Message对象;

       以下通过代码详细看一下这个过程:

public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
} public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

 

  看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message。但实际上都是Message的形式来描写叙述。

这跟我通常理解的消息机制不同:

  通常post消息是将消息增加到消息队列中并不马上运行就返回,send消息是马上运行等待消息运行完才返回。而这里post或者send都是将消息放入到消息队列中。然后马上返回,等待消息循环时获取消息被运行。

  这里提供了众多的消息发送方法来指定消息的运行时间和顺序。详细能够查看源代码。

消息运行顺序是依据消息队列中消息的排列顺序而定。以下看一下发送消息后将消息增加到消息队列中的代码:

由Handler调用MessageQueue的enqueueMessage方法:

       

  final boolean enqueueMessage(Message msg, long when) {

              Message p = mMessages;

              if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
}
else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
} msg.next = prev.next;
prev.next = msg;
}
……
  }

  能够看到是依照时间顺序将消息增加到MessageQueue中;

如今将消息增加到消息队列中存储起来。消息并未得到处理。下一步必定是怎样派发消息和处理消息。

3 消息派发

建立Thread消息循环由Looper完毕,存在一个消息调度死循环:    

  public static void loop() {
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
} //派发消息 到target(Handler)
            msg.target.dispatchMessage(msg);             //回收Msg到msgPool
msg.recycle();
}
}
  }

  这里看到消息派发是由Message的target完毕,这个target是什么呢?是一个Handler。消息系统是通过Handler用来与外部交互,把消息派发出去。能够看到没有这个Handler,消息循环将结束。

消息派发由Looper通过Handler完毕:

  public void dispatchMessage(Message msg) {

       //首先推断runnable对象
if (msg.callback != null) {
handleCallback(msg);
}
else {
//整个消息系统的回调函数 能够不用实现自己Handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//消息处理 通常交给Handler派生类
handleMessage(msg);
}
  }

  通过消息派发,这样就实现消息的异步处理。

4 消息原型

前面看到消息发送有两种方式:

  post(Runnable对象),sendMessage(Message对象)。而中间都是通过Message对象

保存在MessageQueue中。然后消息派发时处理方式不同。假设在sendMessage时将将消息对象附上Runnable对象,则post和sendMessage没有差别了。

所以这两种方式非常好理解基本一致。处理的方式不同罢了。

  消息系统模型中,我们的真正的消息原型是什么。都具有那些功能,以下看一下Message中究竟包括了那些东西,能有效帮助我们合理的运用消息系统来完毕一些任务和处理。

Message消息原型:

  public final class Message implements Parcelable {
  //标识消息
  public int what;
  int flags;
  long when;   //传递简单数据
   public int arg1;
  public int arg2;   //传递较复杂数据 对象
  public Object obj;
  Bundle data;   //处理消息的目标Handler
  Handler target;   //消息派发时 运行的Runnable对象
  Runnable callback;   //使消息形成链表
  Message next;   //建立一个消息pool,回收msg,以避免反复创建节约开销
  private static Message sPool;
  private static int sPoolSize = 0;
  private static final int MAX_POOL_SIZE = 10;
  }  

  看到提供了非常丰富的属性来描写叙述消息,针对详细问题选择使用那些属性去怎么样描写叙述消息了。

获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,通过obtain获取,可能从MessagePool中获取。节约开销。

以下看一下这个MessagePool是怎样建立的:

  通常消息处理完毕的时候。消息也基本上处于无用状态能够释放回收了。对于须要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这样的非常有可能会频繁的创建。

于是我们能够将创建的对象用完之后保存在一个Pool里面,以便再反复利用节约频繁创建释放开销。

是怎样建立的呢?必定是在消息处理完毕之后才干进行。

MessagePool建立:

public static void loop() {
while (true) {
//派发消息
msg.target.dispatchMessage(msg); //消息处理完毕 回收
        msg.recycle();
    }
} public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}

五 Handler的应用

  以上这就是整个Handler作用及消息系统模型的建立。

使用也非常easy,尽管有非常多方式,但仅仅要理解Handler是建立在Looper上,实现Thread的消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。其它方面能够去看源代码了。

  Handler使用起来是非常easy的,关键就是怎样利用消息的异步处理。来合理的完毕我们须要功能和任务。

对于一个Thread,我们使用好几个Handler来进行异步处理。也能够创建新的Thread,通过Handler来实现消息异步处理等等,应用场景非常多怎样用的好用的合理,这就没什么经验了。

Handler有何作用?怎样使用?的更多相关文章

  1. Handler.removeMessages的作用,有时候为什么一定要先remove一下呢

    removeMessages会将handler对应message queue里的消息清空,如果带了int参数则是对应的消息清空.队列里面没有消息则handler会不工作,但不表示handler会停止. ...

  2. 阶段一:用Handler和Message实现计时效果及其中一些疑问

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 本来是打算继续做天气预报的优化的,但因为某些原因,我要先把之前做的小应用优化一下.所以今天就插播一下用Handle ...

  3. 讲讲Handler+Looper+MessageQueue 关系

    Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...

  4. 深入理解Message, MessageQueue, Handler和Looper

    做过Android的都知道Message, MessageQueue, Handler和Looper,但知道不代表你理解它们.有时觉得用得很顺手,但Android怎么实现又说不上来,总觉得似懂非懂.不 ...

  5. 学习通过Thread+Handler实现非UI线程更新UI组件

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

  6. Android子线程更新UI主线程方法之Handler

    背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. ...

  7. android handler工作原理

    android handler工作原理 作用 便于在子线程中更新主UI线程中的控件 这里涉及到了UI主线程和子线程 UI主线程 它很特别.通常我们会认为UI主线程将页面绘制完成,就结束了.但是它没有. ...

  8. Android菜鸟的成长笔记(12)——Handler、Loop、MessageQueue

    原文:[置顶] Android菜鸟的成长笔记(12)——Handler.Loop.MessageQueue 当一个程序第一次启动时,Android会启动一条主线程(Main Thread),主线程主要 ...

  9. Handler和HandlerThread

    1.什么是Handler? SDK中关于Handler的说明例如以下: A Handler allows you to sendand process Messageand Runnable obje ...

随机推荐

  1. 如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2'

    如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2' 大家在做真机调试的时候,或许会遇到这样的问题,那如何 ...

  2. Codeforces Round #360 (Div. 2)——C. NP-Hard Problem(BFS染色判二分图)

    C. NP-Hard Problem time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  3. HDU 5352 MZL's City (2015 Multi-University Training Contest 5)

    题目大意: 一个地方的点和道路在M年前全部被破坏,每年可以有三个操作, 1.把与一个点X一个联通块内的一些点重建,2.连一条边,3.地震震坏一些边,每年最多能重建K个城市,问最多能建多少城市,并输出操 ...

  4. 【kmp+最小循环节】poj 2406 Power Strings

    http://poj.org/problem?id=2406 [题意] 给定字符串s,s=a^n,a是s的子串,求n最大是多少 [思路] kmp中的next数组求最小循环节的应用 例如 ababab ...

  5. java 数据库连接的几个步骤

    Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:thin:@你的主 ...

  6. Swift3.0 函数闭包与 Block

    刚接触Swift,如有不对,欢迎指正 Swift中定义一个基本函数 //定义一个函数,接收一个字符串,返回一个String类型的值 func test(name:String) -> Strin ...

  7. 互不侵犯King(bzoj 1087)

    Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包 ...

  8. python模块(二)

    一.json模块 作用: 用于[字符串]和 [python基本数据类型] 间进行转换 Python的Json模块序列化与反序列化的过程分别是 encoding和 decoding. encoding ...

  9. Redis命令行之Zset

    一.Redis之Zset简介 1. 有序集合Zset是String类型的有序集合. 2. Zset中每个元素都会关联一个double类型的分数值,redis通过分数值来为集合中所有成员进行从小到大排序 ...

  10. AC日记——A+B Problem(再升级) 洛谷 P1832

    题目背景 ·题目名称是吸引你点进来的 ·实际上该题还是很水的 题目描述 ·1+1=? 显然是2 ·a+b=? 1001回看不谢 ·哥德巴赫猜想 似乎已呈泛滥趋势 ·以上纯属个人吐槽 ·给定一个正整数n ...