Android中Handle详解

时间:2023-02-22 21:11:58

Android中Handle详解

上图为本人总结的Handler,网上发现一片总结很好的博客就copy过来;作为参考

Handler有何作用?如何使用?

一 、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.消息处理

大致模型图如下:

Android中Handle详解

消息系统模型一般会包括以上七个部分( 消息原型,消息队列,消息发送,消息循环, 消息获取,消息派发,消息处理 )。实际上的核心是消息队列和消息循环 ,其余部分都是围绕这两部分进行的。从前面文档的分析中我们知道Handler就是用来建立消息处理 的系统模型,那么和这里基本消息系统模型相比,那么Handler又是如何囊括这七个部分的呢?

Android中对这六个部分进行了抽象成四个独立的部分( 消息循环 ):Handler,Message,MessageQueue,Looper;

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

Handler能够实现消息系统模型,那么具体是如何进行工作的呢,下面探究一下这其中工作的方法和原理。

三、 Handler工作原理分析

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

Android中Handle详解

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的关系可以用下面图来表示:

Android中Handle详解

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完成:

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

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

4 消息原型

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作用及消息系统模型的建立。使用也非常简单,虽然有很多方式,但只要理解Handler是建立在Looper上,实现Thread的 消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。 其他方面可以去看源码了。Handler使用起来是非常简单的,关键就是如何利用消息的异步处理,来合理的完成我们 需要功能和任务。 对于一个Thread,我们使用好几个Handler来进行异步处理,也可以创建新 的Thread, 通过Handler来实现消息异步处理等等,应用场景很多如何用的好用的合理,这就没什么经验了。

 

Android中Handle详解的更多相关文章

  1. Android中Context详解 ---- 你所不知道的Context(转)

    Android中Context详解 ---- 你所不知道的Context(转)                                               本文出处 :http://b ...

  2. Android中Context详解 ---- 你所不知道的Context

    转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好,  ...

  3. android中getSystemService详解

        android的后台运行在很多service,它们在系统启动时被SystemServer开启,支持系统的正常工作,比如MountService监 听是否有SD卡安装及移除,ClipboardS ...

  4. Android中 ListView 详解(二)

    本文版权归 csdn noTice501 所有,转载请详细标明原作者及出处,以示尊重! 作者:noTice501 原文:http://blog.csdn.net/notice520/article/d ...

  5. Android中Context详解

    大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中时刻的在与它打交道,例如:Service.BroadcastReceiver.A ...

  6. 转:Android中Context详解 ---- 你所不知道的Context

    转:http://blog.csdn.net/qinjuning/article/details/7310620 转:http://blog.csdn.net/lmj623565791/article ...

  7. android中LayoutInflater详解与使用

    android的LayoutInflater用来得到一个布局文件,也就是xxx.xml,而我们常用的findviewbyid是用来取得布局文件里的控件或都布局.inflater即为填充的意思,也就是说 ...

  8. android中Actionbar详解

    1.什么是Action BarAction Bar被认为是新版Android系统中最重要的交互元素,在程序运行中一直置于顶部,主要起到的作用在于:1)突出显示一些重要操作(如“最新”.“搜索”等)2) ...

  9. Android中IntentService详解

    简单说,IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service ...

随机推荐

  1. spring&lowbar;01

    1.框架 1.框架是解决什么问题的? 1.框架是用来解决代码冗余的问题 2.有利于团队的协作开发 3.框架是用来解决低耦合和高内聚的问题 4.解决健壮性和安全性 2.STRUTS2和hibernate ...

  2. Zookeeper&comma;Hbase 伪分布&comma;集群搭建

    工作中一般使用的都是zookeeper和Hbase的分布式集群. more /etc/profile cd /usr/local zookeeper-3.4.5.tar.gz zookeeper在安装 ...

  3. leetcode 题解 &vert;&vert; Swap Nodes in Pairs 问题

    problem: Given a linked list, swap every two adjacent nodes and return its head. For example, Given ...

  4. 嵌入式linux的网络编程&lpar;1&rpar;--TCP&sol;IP协议概述

    嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...

  5. html5权威指南:客户端分区响应图

    客户端分区响应图:http://www.cnblogs.com/enjoymylift/p/5984338.html

  6. PMD教程

    1.单词 violations outline:错误大纲2.错误级别 红色 很高的错误 橙色 错误 黄色 很高的警告 绿色 警告 蓝色 输出信息3.提示 Avoid excessively long ...

  7. storm-starter 例子学习

    storm-starter 是 apache storm 针对初学者的一个用maven管理的项目示例.这篇文章记录storm-starter例子代码分析及执行. 参考:https://github.c ...

  8. 基于node&period;js的web框架express

    1.安装node.js方法: window :https://nodejs.org/en/ linux:http://www.runoob.com/nodejs/nodejs-install-setu ...

  9. postgres数据库参数配置说明介绍

    访问 1. listen_addresses 监听访问地址 2. port 监听端口 3. max_connections 最大连接数 4. 性能 1. shared_buffers PostgreS ...

  10. Spring中的BeanFactory和ApplicationContext的区别

    我用一个例子去测试BeanFactory和ApplicationContext的区别 首先建立一个bean public class User { //声明无参构造,打印一句话,监测对象创建时机 pu ...

相关文章