Android开发手记(28) Handler和Looper
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的更多相关文章
- Android 开发 深入理解Handler、Looper、Messagequeue 转载
转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/73484527 本文已授权微信公众号 fanfan程序媛 独家发布 扫一扫文章底 ...
- android开发笔记:Handler、Looper、MessageQueen、Message的关系
一.什么是handler? 注:线程分为主线程(主线程又叫UI线程,只能有一个主线程)和子线程(可以有多个)Handler只能在主线程里运行 handler是Android给我们提供用来更新UI的一套 ...
- Android开发 之 理解Handler、Looper、MessageQueue、Thread关系
本文转自博客:http://blog.csdn.net/he90227/article/details/43567073 一. 图解与概述 首先Android中 的每一个线程都会对应一个Message ...
- Android源码解析——Handler、Looper与MessageQueue
本文的目的是来分析下 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制,通过源码来了解整个消息处理流程的走向以及相关三者之间的关系 需要先了解以 ...
- 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue
解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...
- Android 开发手记一NDK编程实例
在Android上,应用程序的开发,大部分基于Java语言来实现.要使用c或是c++的程序或库,就需要使用NDK来实现.NDK是Native Development Kit的简称.它是一个工具集,集成 ...
- Android开发笔记之:Handler Runnable与Thread的区别详解
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一 个类只要继承了Thread类同时覆写了本类中的run( ...
- Android开发之异步通信Handler机制
郭大神的:http://blog.csdn.net/guolin_blog/article/details/9991569 http://www.jianshu.com/p/08cb3665972f ...
- Android开发之使用Handler封装下载图片工具类(源码分享)
假设每下载一张图片,就得重写一次Http协议,多线程的启动和handler的信息传递就显得太麻烦了,我们直接来封装一个工具类,便于我们以后在开发时随时能够调用. (1)在清单文件加入权限 <us ...
随机推荐
- 实验一个最小的PYTHON服务器编程
没事,玩玩儿~~~:) 按书上的例子来作.. #!/usr/bin/env python #Simple Server - Chapter 1 -server.py import socket hos ...
- android——彻底关闭——应用程序
最近学习做android的游戏开发时候,发现一个关于android退出时不能彻底关闭的问题,比如:一个程序里new 出了N多个Thread,这样在退出程序的可能不能完全关闭,最后发现,只用finish ...
- mysql优化21条经验(转)
今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序 员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...
- Reverse Linked List 递归非递归实现
单链表反转--递归非递归实现 Java接口: ListNode reverseList(ListNode head) 非递归的实现 有2种,参考 头结点插入法 就地反转 递归的实现 1) Divide ...
- 14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发
14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发 InnoDB 使用操作系统线程来处理请求(用户事务) 事务可能执行很多次在它们提交或者回 ...
- BZOJ1617: [Usaco2008 Mar]River Crossing渡河问题
1617: [Usaco2008 Mar]River Crossing渡河问题 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 654 Solved: 4 ...
- 如何查看jar包的版本号?
jar包根目录里的META-INF目录下的MANIFEST.MF文件里一般有会记录版本信息,可以到这个文件里查看 打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录,这个 ...
- 如何配置jdk和tomcat 转
一.配置JDK1.解压JDK至D:\JDK1.5目录下(楼主可以自由选取目录).2.设置环境变量(右键我得电脑->属性->高级->环境变量),在系统变量中添加一个叫JAVA_HOME ...
- ubuntu下使用ngrok外网映射
好久之前想搞明白这个事情,可是就是不知道这个词叫外网映射,所以也一直不知怎么做,在慕课网看用java开发微信公众号的时候教程里提到了外网映射,查了一些资料终于把本地给映射到外网了,直接变成了80端口, ...
- 【PHP】将EXCEL表中的数据轻松导入Mysql数据表
在网络上有不较多的方法,在此介绍我已经验证的方法. 方法一.利用EXCEL表本身的功能生成SQL代码 ①.先在“phpmyadmin”中建立数据库与表(数据库:excel,数据表:excel01,字段 ...