Android的消息处理有三个核心类:Looper,Handler和Message。其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道。平时我们最常使用的就是Message与Handler了,如果使用过HandlerThread或者自己实现类似HandlerThread的东西可能还会接触到Looper,而MessageQueue是Looper内部使用的,对于标准的SDK,我们是无法实例化并使用的(构造函数是包可见性)。

在Android中,消息处理的大体过程为:

(1)首先,要有消息的内容,我们将其放到一个Bundle中来存储消息的内容。

(2)然后通过此Bundle来实例化一个Message,并将此Message作为消息发送的基本单位。

(3)之后可以将Message放到Looper中用以循环处理消息。

(4)由于Handler内部拥有Looper实例,所以可以直接通过Handler发送和处理上述的Message。

以上就是Android中消息发送和处理的基本过程。

  以下1、2部分引自http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html

1、Looper

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare(); // ...其他处理,如实例化handler // 开始循环处理消息队列
Looper.loop();
}
}

通过上面两行核心代码,你的线程就升级为Looper线程了!!!是不是很神奇?让我们放慢镜头,看看这两行代码各自做了什么。

1)Looper.prepare()

通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象。

2)Looper.loop()

调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。由上loop()方法可以简单得出结论,Looper用以处理MessageQueue中的取出的Message,由MessageQueue是Handler及Looper所共用的,取出的Message则交由Handler进行处理。而Handler也能够通过post或者send等方式将Message添加到MessageQueue中供Looper后续进行取出处理。sThreadLocal保证了Looper是线程私有的,所有信息发送与处理都是在本线程中。

prepare()用以在sThreadLocal中创建线程与该线程对应的Looper的键值对;new Handler或者getHandler创建的Handler都根据sThreadLocal.get()进行获取;创建的Handler与Looper共用MessageQueue;loop开始循环处理Messagequeue中的事件。其即为整个流程。

2、Handler

handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。加入handler后的效果如下图:

可以看到,一个线程可以有多个Handler,但是只能有一个Looper!

3、使用Handler发送和处理消息

根据本文开始提到的,我们来实现通过Handler发送消息,然后改变MainActivity内一个TextView的颜色和内容。规定 flag == false 时为红色,arg1 = 0;flag == true 时为蓝色,arg1 = 1。

我们需要明白,Handler是通过一个进程来发送消息的,所以我们需要将发送消息的过程写到Runnable.run()方法内。首先我们新建一个SendMessage implements Runnable 类,然后我们构造消息:

    @Override
public void run(){
while(true) {
try {
Thread.sleep(1000); // 每个1秒启动一次消息发送
Bundle bundle = new Bundle(); // Bundle 用于存储消息内容
Message msg = Message.obtain(); // 实例化一个空消息
if (flag) { // 如果flag为真,说明当前颜色为蓝色
msg.arg1 = 0; // 颜色应变为红色,记录信息用于发送
flag = false; // 假设当前已经变为红色
bundle.putString("text", "我的颜色是红色");
} else { // 如果flag为假,说明当前颜色为红色
msg.arg1 = 1; // 颜色应变为蓝色,记录信息用于发送
flag = true; // 假设当前已经变为蓝色
bundle.putString("text", "我的颜色是蓝色");
}
count++; // 记录颜色变化的次数
msg.arg2 = count; // 存储颜色变化的次数用以发送
msg.setData(bundle); // 将Bundle封装至待发送的消息
handler.sendMessage(msg); // 通过handler发送消息
} catch (Exception e) {
e.printStackTrace();
}
}
}

这里需要注意的是,我们在通过bundle发送消息的时候,发送到什么地方了呢?这里我们需要在MainActivity内实例化一个Handler来处理发送的消息,并同时用此Handler来构造SendMessage。

class SendMessage implements Runnable{
private Handler handler;
static boolean flag = false;
static int count = 0; public SendMessage(Handler handler){
this.handler = handler;
} @Override
public void run(){
while(true) {
try {
Thread.sleep(1000); // 每个1秒启动一次消息发送
/* ----------------------------------------- */
handler.sendMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
} }

下面就是在MainActivity内进行消息处理,我们继承Handler来新建一个自己的消息处理器。通过Message.getData().getString(String keyValue)来获取我们上一步中封装到Message内的Bundle信息。通过判断传入Message的arg1和arg2的简单int类型,来判断需要改变的颜色。(如果待发送的消息比较简单的话,可以直接封装到Message的arg1或者arg2内,就不必再使用Bundle来发送消息了)。

	class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
textView.setText(msg.getData().getString("text") + msg.arg2);
if(msg.arg1 == 1)
textView.setTextColor(Color.BLUE);
else
textView.setTextColor(Color.RED);
} }

最后,为Button添加单击事件,来启动SendMessage线程即可。

public class MainActivity extends Activity {

    private Button button;
private TextView textView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button);
textView = (TextView)findViewById(R.id.textView); button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Thread thread = new Thread(new SendMessage(new MyHandler()));
thread.start();
}
});
} class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
textView.setText(msg.getData().getString("text") + msg.arg2);
if(msg.arg1 == 1)
textView.setTextColor(Color.BLUE);
else
textView.setTextColor(Color.RED);
} } }

完整代码如下:

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Message;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { private Button button;
private TextView textView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button);
textView = (TextView)findViewById(R.id.textView); button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Thread thread = new Thread(new SendMessage(new MyHandler()));
thread.start();
}
});
} class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
textView.setText(msg.getData().getString("text") + msg.arg2);
if(msg.arg1 == 1)
textView.setTextColor(Color.BLUE);
else
textView.setTextColor(Color.RED);
} } } class SendMessage implements Runnable{
private Handler handler;
static boolean flag = false;
static int count = 0; public SendMessage(Handler handler){
this.handler = handler;
} @Override
public void run(){
while(true) {
try {
Thread.sleep(1000); // 每个1秒启动一次消息发送
Bundle bundle = new Bundle(); // Bundle 用于存储消息内容
Message msg = Message.obtain(); // 实例化一个空消息
if (flag) { // 如果flag为真,说明当前颜色为蓝色
msg.arg1 = 0; // 颜色应变为红色,记录信息用于发送
flag = false; // 假设当前已经变为红色
bundle.putString("text", "我的颜色是红色");
} else { // 如果flag为假,说明当前颜色为红色
msg.arg1 = 1; // 颜色应变为蓝色,记录信息用于发送
flag = true; // 假设当前已经变为蓝色
bundle.putString("text", "我的颜色是蓝色");
}
count++; // 记录颜色变化的次数
msg.arg2 = count; // 存储颜色变化的次数用以发送
msg.setData(bundle); // 将Bundle封装至待发送的消息
handler.sendMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
} }

Android开发手记(28) Handler和Looper的更多相关文章

  1. Android 开发 深入理解Handler、Looper、Messagequeue 转载

    转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/73484527 本文已授权微信公众号 fanfan程序媛 独家发布 扫一扫文章底 ...

  2. android开发笔记:Handler、Looper、MessageQueen、Message的关系

    一.什么是handler? 注:线程分为主线程(主线程又叫UI线程,只能有一个主线程)和子线程(可以有多个)Handler只能在主线程里运行 handler是Android给我们提供用来更新UI的一套 ...

  3. Android开发 之 理解Handler、Looper、MessageQueue、Thread关系

    本文转自博客:http://blog.csdn.net/he90227/article/details/43567073 一. 图解与概述 首先Android中 的每一个线程都会对应一个Message ...

  4. Android源码解析——Handler、Looper与MessageQueue

    本文的目的是来分析下 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制,通过源码来了解整个消息处理流程的走向以及相关三者之间的关系 需要先了解以 ...

  5. 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue

    解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...

  6. Android 开发手记一NDK编程实例

    在Android上,应用程序的开发,大部分基于Java语言来实现.要使用c或是c++的程序或库,就需要使用NDK来实现.NDK是Native Development Kit的简称.它是一个工具集,集成 ...

  7. Android开发笔记之:Handler Runnable与Thread的区别详解

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一 个类只要继承了Thread类同时覆写了本类中的run( ...

  8. Android开发之异步通信Handler机制

    郭大神的:http://blog.csdn.net/guolin_blog/article/details/9991569 http://www.jianshu.com/p/08cb3665972f ...

  9. Android开发之使用Handler封装下载图片工具类(源码分享)

    假设每下载一张图片,就得重写一次Http协议,多线程的启动和handler的信息传递就显得太麻烦了,我们直接来封装一个工具类,便于我们以后在开发时随时能够调用. (1)在清单文件加入权限 <us ...

随机推荐

  1. 实验一个最小的PYTHON服务器编程

    没事,玩玩儿~~~:) 按书上的例子来作.. #!/usr/bin/env python #Simple Server - Chapter 1 -server.py import socket hos ...

  2. android——彻底关闭——应用程序

    最近学习做android的游戏开发时候,发现一个关于android退出时不能彻底关闭的问题,比如:一个程序里new 出了N多个Thread,这样在退出程序的可能不能完全关闭,最后发现,只用finish ...

  3. mysql优化21条经验(转)

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序 员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...

  4. Reverse Linked List 递归非递归实现

    单链表反转--递归非递归实现 Java接口: ListNode reverseList(ListNode head) 非递归的实现 有2种,参考 头结点插入法 就地反转 递归的实现 1) Divide ...

  5. 14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发

    14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发 InnoDB 使用操作系统线程来处理请求(用户事务) 事务可能执行很多次在它们提交或者回 ...

  6. BZOJ1617: [Usaco2008 Mar]River Crossing渡河问题

    1617: [Usaco2008 Mar]River Crossing渡河问题 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 654  Solved: 4 ...

  7. 如何查看jar包的版本号?

    jar包根目录里的META-INF目录下的MANIFEST.MF文件里一般有会记录版本信息,可以到这个文件里查看   打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录,这个 ...

  8. 如何配置jdk和tomcat 转

    一.配置JDK1.解压JDK至D:\JDK1.5目录下(楼主可以自由选取目录).2.设置环境变量(右键我得电脑->属性->高级->环境变量),在系统变量中添加一个叫JAVA_HOME ...

  9. ubuntu下使用ngrok外网映射

    好久之前想搞明白这个事情,可是就是不知道这个词叫外网映射,所以也一直不知怎么做,在慕课网看用java开发微信公众号的时候教程里提到了外网映射,查了一些资料终于把本地给映射到外网了,直接变成了80端口, ...

  10. 【PHP】将EXCEL表中的数据轻松导入Mysql数据表

    在网络上有不较多的方法,在此介绍我已经验证的方法. 方法一.利用EXCEL表本身的功能生成SQL代码 ①.先在“phpmyadmin”中建立数据库与表(数据库:excel,数据表:excel01,字段 ...