安卓开发_深入理解Handler消息传递机制
一、概述
因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面
二、消息类(Message)
消息类是存放在MessageQueue中的,而一个MessageQueue中可以包含多个Message对象
每一个Message对象可以通过Message.obtain()或者Handler.obtainMessage()方法获得
一个Message具有的属性:
| 
 属性  | 
 类型  | 
 介绍  | 
| 
 arg1  | 
 int  | 
 存放整型数据  | 
| 
 arg2  | 
 int  | 
 存放整型数据  | 
| 
 obj  | 
 Object  | 
 存放Object类型的任意对象  | 
| 
 replyTo  | 
 Message  | 
 指定此Message发送到哪里的可选Message对象  | 
| 
 what  | 
 int  | 
 指定用户自定义的消息代码,接受者可以了解这个消息的信息  | 
一个Message对象可以携带int类型的数据,而如果要携带其他类型的数据,可以将要携带的数据保存到Bundle对象中,然后通过Message类的setDate()方法将其添加到Message中
注:
1、尽量使用Message.what标识信息,方便用于不同的方式处理Message
2、通常情况下,尽量使用Message.obtain()或者Handler.obtainMessage()方法从消息池中获得空消息对象,以便节省资源,而不是使用Message的默认构造方法
3、当一个Message对象只需要携带int型数据的时候,优先使用Message.arg1或Message.arg2属性,这要比用Bundle携带int数据节省内存
三、消息处理类(Handler)
Handler 允许 发送或者处理 Message或者Runnable 类的对象到其(Handler)所在线程的MessageQueue中
主要有两个作用:
1、连接主线程和子线程进行通信(UI线程和工作线程通信)
2、将Message对象 通过post()或者sendMessage()方法发送到MessageQueue中,
当MessageQueue循环到该对象时,调用相应的Handler对象的handlerMessage()方法进行处理
Handler类提供的部分方法:
| 
 方法  | 
 介绍  | 
| 
 handleMessage(Message msg)  | 
 处理消息的方法。 通常使用该方法处理消息, 在发送消息时,该方法会自动回调  | 
| 
 Post(Runnable r)  | 
 立即发送Runnable对象, 注:该Runnable对象最终将被封装成Mwssage对象  | 
| 
 PostAtTime(Runnable r,long uptimeMillis)  | 
 定时发送Runnable对象, 注:该Runnable对象最终将被封装成Mwssage对象  | 
| 
 postDelayed(Runnable r,long delayMillis)  | 
 延迟发送Runnable对象, 注:该Runnable对象最终将被封装成Mwssage对象  | 
| 
 sendEmptyMessage(int what)  | 
 发送空消息  | 
| 
 sendMessage(Message msg)  | 
 立即发送消息  | 
| 
 sendMessageAtTime(Message msg)  | 
 定时发送消息  | 
| 
 sendMessageDelayed(Message msg,long delayMillis)  | 
 延迟发送消息  | 
注:在一个线程中,只能有一个Looper和MessageQueue,却可以有多个Handler,这些Handler共享同一个Looper和MessageQueue
四、循环着(Looper)
1、从上面只是可以知道:一个线程中,只能有一个Looper和MessageQueue。
也就是说一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列),一个MessageQueue又存放着多条Message
注意:MessageQueue中,消息的存放时FIFO(先进先出)的。
2、Looper对象是为了一个线程开启一个消息循环,来操作MessageQueue
注:在主线程中,系统会为主线程自动创建一个Looper对象来开启消息循环。而在非主线程中,是没有Looper对象的,没有Looper对象就不能创建Handler对象
so,在主线程中直接创建Handler对象是可以的。但是在非主线程中创建Handler对象则会产生异常
3、如果需要在非主线创建Handler对象
(1)使用Looper类的prepare()方法来初始化一个Looper对象
(2)创建Handler对象
(3)使用Looper类的loop()方法启动Looper,开启消息循环
//需要先有Looper对象
Looper.prepare();//会创建一个Looper对象,并把该对象放入到该线程的本地变量中,在Looper的构造方法中创建了MessageQueue对象
//在子线程中实例化handler,子线程中没有Looper对象 handler = new Handler(){ };//如果直接实例化Handler,会异常 new RuntimeException,原因是子线程中没有Looper对象 //让Looper对象循环读取MessageQueue中的消息
//循环从队列中读取消息,当读到消息时,会去调用 msg.target.dispatchMessage(msg);
//在Message类中有一个target成员,当发送消息时,target成员被赋值为当前的 handler对象
Looper.loop();
Looper提供的部分方法:
| 
 方法  | 
 描述  | 
| 
 prepare()  | 
 用于初始化Looper  | 
| 
 loop()  | 
 启动Looper线程,线程循环消息队列(MessageQueue)获取和处理信息  | 
| 
 myLooper()  | 
 获得当前线程的Looper对象  | 
| 
 getThread  | 
 获得Looper对象所属的线程  | 
| 
 quit()  | 
 结束Looper循环  | 
*注*:Looper.loop()方法,这个方法是循环消息队列!即这个方法内部相当于正在执行一个死循环,所以在Looper.loop()代码之后的代码都不会执行
,而只有再调用 Looper.quit()之后才会执行后面的代码
-------------------------------------------------------------华丽的分割线------------------------------------------------------------------
让我们看几个例子来深入理解下Handler消息传递机制
1、子线程向主线程发送消息
在主线程中启动一个子线程下载图片,子线程传消息递给主线程,让主线程处理。(了解子线程发送Runnable对象和发送Message对象的两种方法)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
> <ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/show_image"
android:src="@drawable/ic_launcher"
/> <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/down_image"
android:text="下载"
/> </LinearLayout>
布局文件代码
(1)发送Message对象的方法
package com.xqx.handle; import java.io.IOException; import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils; import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView; public class MainActivity extends Activity {
private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//图片下载地址
private ImageView showImage;
private Button downImage;
//哪一个线程是接受消息的,就在哪个线程中实例化Handler对象
//因为主线程中,系统会为自动创建Looper对象,开启循环消息,所以只需要在主线程中定义Handler对象
private Handler handler = new Handler(){
@Override //处理消息的方法,当消息发送过来时,该方法自动回调
public void handleMessage(android.os.Message msg) {
//处理方法,将图片显示在ImageView中
showImage.setImageBitmap((Bitmap) msg.obj); };
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); showImage = (ImageView) findViewById(R.id.show_image);
downImage = (Button) findViewById(R.id.down_image); downImage.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
//开启一个线程下载图片
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
HttpGet get = new HttpGet(path);
HttpClient client = new DefaultHttpClient();
HttpResponse response = null; try {
response = client.execute(get);
if(response.getStatusLine().getStatusCode()==)
{
//获得下载后的图片的字节数据
byte b[] = EntityUtils.toByteArray(response.getEntity());
//将图片字节数据转换成Bitmap格式
Bitmap bitmap = BitmapFactory.decodeByteArray(b, , b.length);
//下载完成,将图片发送给主线程
//从MessageQueue中获取可用的Message对象,如果没有可用的,则会创建一个新的Message对象
Message msg = Message.obtain();
//把发送的图片放到msg对象中
msg.obj = bitmap;
//使用Handler.sendMessage(Message msg)方法传递消息
handler.sendMessage(msg);
} } catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}).start();//不要忘记开启线程
}
}); } }
Handler.sendMessage(Message msg)
(2)、发送Runnable对象的方法
package com.xqx.handle; import java.io.IOException; import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils; import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView; public class Handler2 extends Activity {
private String path = "http://images2015.cnblogs.com/blog/493196/201509/493196-20150911220222090-1272536050.jpg";//图片下载地址
private ImageView showImage;
private Button downImage;
//哪一个线程是接受消息的,就在哪个线程中实例化Handler对象
//因为主线程中,系统会为自动创建Looper对象,开启循环消息,所以只需要在主线程中定义Handler对象
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); showImage = (ImageView) findViewById(R.id.show_image);
downImage = (Button) findViewById(R.id.down_image); downImage.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
//开启一个线程下载图片
new Thread(new Runnable() { private Bitmap bitmap; @Override
public void run() {
// TODO Auto-generated method stub
HttpGet get = new HttpGet(path);
HttpClient client = new DefaultHttpClient();
HttpResponse response = null; try {
response = client.execute(get);
if(response.getStatusLine().getStatusCode()==)
{
//获得下载后的图片的字节数据
byte b[] = EntityUtils.toByteArray(response.getEntity());
bitmap = BitmapFactory.decodeByteArray(b, , b.length);
//下载完成,将图片发送给主线程
//Handler.post(Runnable r)方法
handler.post(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
showImage.setImageBitmap(bitmap);
}
});
} } catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}).start();//不要忘记开启线程
}
}); } }
Handler.post(Runnable r)
最后因为下载网络图片,不要忘记在清单文件中 添加网络访问权限
<uses-permission android:name="android.permission.INTERNET"/>
效果图:
下载前:

下载后:

2、主线程向子线程发送消息
布局还是上面的那个,就不贴代码了
package com.xqx.handle; import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast; public class Handler3 extends Activity{ private Button changeImage;
private ImageView showImage;
private Handler handler; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); changeImage = (Button) findViewById(R.id.down_image);
showImage = (ImageView) findViewById(R.id.show_image);
MyThread mt = new MyThread();
Thread h = new Thread(mt);
h.start();
changeImage.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
//主线程发送消息
Message msg = Message.obtain();
msg.what = ;
handler.sendEmptyMessage();
}
}); } class MyThread implements Runnable
{ @Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
int number = msg.what;
Toast.makeText(getApplicationContext(), "子线程接收到了主线程的消息,消息内容为:"+number, ).show();
}
};
Looper.loop();
} } }
主线程项子线程发送消息
效果图:

安卓开发_深入理解Handler消息传递机制的更多相关文章
- 安卓开发_深入理解Content Provider
		
一.Content Provider概述 Content Provider用于保存和获取数据,并使其对所有应用程序可见,这是不同应用程序之间共享数据的唯一方式,因为在Android中没有提供所有应用可 ...
 - 安卓开发_深入理解Activity和Fragment的关系
		
Fragment(碎片)是必须嵌入在 Activity(活动) 中使用的.Fragment的生命周期随着Activity的生命周期的变化而变化 一.首先让我们看下Activity和Fragment的生 ...
 - Android学习笔记-事件处理之Handler消息传递机制
		
内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ...
 - Android开发:图文分析 Handler通信机制 的工作原理
		
前言 在Android开发的多线程应用场景中,Handler机制十分常用 下面,将图文详解 Handler机制 的工作原理 目录 1. 定义 一套 Android 消息传递机制 2. 作用 在多线程的 ...
 - 安卓开发_深入学习ViewPager控件
		
一.概述 ViewPager是android扩展包v4包(android.support.v4.view.ViewPager)中的类,这个类可以让用户左右切换当前的view. ViewPager特点: ...
 - 安卓开发_浅谈ListView(SimpleAdapter数组适配器)
		
安卓开发_浅谈ListView(ArrayAdapter数组适配器) 学习使用ListView组件和SimapleAdapter适配器实现一个带图标的ListView列表 总共3部分 一.MainAc ...
 - 安卓开发_浅谈Android动画(四)
		
Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1. ValueAnimator 基本属 ...
 - 安卓开发_数据存储技术_sqlite
		
一.SQLite SQLite第一个Alpha版本诞生于2000年5月,它是一款轻量级数据库,它的设计目标是嵌入式的,占用资源非常的低,只需要几百K的内存就够了.SQLite已经被多种软件和产品使用 ...
 - 安卓开发_浅谈ListView(自定义适配器)
		
ListView作为一个实际开发中使用率非常高的视图,一般的系统自带的适配器都无法满足开发中的需求,这时候就需要开发人员来自定义适配器使得ListView能够有一个不错的显示效果 有这样一个Demo ...
 
随机推荐
- Eclipse上搭建Spring的开发环境
			
一.安装Spring Tool Suite插件 如图: 点击Finish之后等待安装,安装完之后弹窗点击yes重启Eclipse,重启后显示如下界面: 二.搭建Spring开发环境 1.导入jar包到 ...
 - LeetCode手记-Add Binary
			
问题描述 问题分析 分析题意,此题实际是求解两个二进制数的和,但是有两点要注意: 1.字符串的长度不限,所以相应十进制数值很可能会超过int的上限. 2.二进制的加法规则是自右向左进位,需要注意,以题 ...
 - 21-json pickle  shelve XML
			
我们把对象从内存中变成可存储或传输的过程称之为序列化 在python中叫picking 在其他语言中也被称之为 serialization marshalling flattening等等 序列化之后 ...
 - Go语言之Interface(一)
			
Go语言之Interface(一) 什么是interface 在面向对象语言中接口是:接口定义了一个对象的行为,但在Go中接口就是方法签名的集合,当一个类型提供了这个接口中的所有的方法,就可以说这个类 ...
 - Python常用模块—— Colorama模块
			
简介 Python的Colorama模块,可以跨多终端,显示字体不同的颜色和背景,只需要导入colorama模块即可,不用再每次都像linux一样指定颜色. 1. 安装colorama模块 pip i ...
 - Java 基础学习总结(一)抽象类和接口
			
接触java的时间不是很长,以前对抽象类和接口的定义和区别也是模糊不清,最近拿起学校的教程读了起来,也参阅了网上的博客大神理解和总结,于是决定自己按照自己的理解来总结一下. 抽象类(半成品) 一般 ...
 - Django--模板层template
			
一 模版简介 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now ...
 - django model数据 时间格式
			
from datetime import datetime dt = datetime.now() print '时间:(%Y-%m-%d %H:%M:%S %f): ' , dt.strftime( ...
 - SpringBoot集成Redis
			
1.引入 spring-boot-starter-redis <dependency> <groupId>redis.clients</groupId> <a ...
 - epoll的ET和LT模式
			
epoll有两种模式,Edge Triggered(简称ET) 和 Level Triggered(简称LT). 在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用 ...