Handler消息传递机制——Handler、Loop、MessageQueue的工作原理
为了更好地理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件。
- Message:Handler接收和处理的消息对象。
- Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读取到消息之后就把消息交给发送该消息的Handler进行处理。
- MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象。Looper提供的构造器源代码如下:
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
该构造器使用了private修饰,表明程序员无法通过构造器创建Looper对象。从上面的代码不难看出,程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。
- Handler:它的作用有两个——发送消息和处理消息,程序使用Handler发送消息,被Handler发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler正常工作,必须在当前线程中有一个MessageQueue,否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象。为了保证当前线程中有Looper对象,可以分如下两种情况处理。
- 主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息、处理消息。
- 程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它 。创建Looper对象调用它的prepare()方法即可。
prepare()方法保证每个线程最多只有一个Looper对象。prepare()方法的源代码如下:
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。下面是Looper类的loop()方法的源代码:
- public static void loop() {
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- final MessageQueue queue = me.mQueue;
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " +
- msg.callback + ": " + msg.what);
- }
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- Log.wtf(TAG, "Thread identity changed from 0x"
- + Long.toHexString(ident) + " to 0x"
- + Long.toHexString(newIdent) + " while dispatching to "
- + msg.target.getClass().getName() + " "
- + msg.callback + " what=" + msg.what);
- }
- msg.recycle();
- }
- }
归纳起来,Looper、MessageQueue、Handler各自的作用如下。
- Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
- MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
- Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
在线程中使用Handler的步骤如下。
- 调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建于之配套的MessageQueue。
- 有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。
- 调用Looper的loop()方法启动Looper。
下面通过实例来介绍Looper与Handler的用法。
实例:使用新线程计算质数
该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将上限数值发送到新启动的线程,该线程来计算该范围内的所有质数。
之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要相应用户动作,如果在UI线程中执行一个”耗时“操作,将会导致UI线程被阻塞,从而让应用程序失去响应。比如在该实例中,如果用户输入的数值太大,系统可能需要较长时间才能计算出所有质数,这就可能导致UI线程失去响应。
为了将用户在UI界面输入的数值上限动态的传给新启动的线程,本实例将会在线程中创建一个Handler对象,然后UI线程的事件处理方法就可以通过该Handler向新线程发送消息了。
该实例的界面布局文件如下:
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <EditText
- android:id="@+id/etNum"
- android:inputType="number"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="请输入上限"/>
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="cal"
- android:text="计算"/>
- </LinearLayout>
该实例的Activity代码如下。
- package com.example.studyevent;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Looper;
- import android.os.Message;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.EditText;
- import android.widget.Toast;
- public class CalPrime extends Activity {
- private final String UPPER_NUM="upper";
- EditText etNum;
- CalThread calThread;
- //定义一个线程类
- class CalThread extends Thread{
- public Handler mHandler;
- public void run()
- {
- Looper.prepare();
- mHandler=new Handler()
- {
- //定义处理消息的方法
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- if(msg.what==0x123)
- {
- int upper=msg.getData().getInt(UPPER_NUM);
- List<Integer> nums=new ArrayList<Integer>();
- //计算从2开始、到upper的所有质数
- outer:
- for(int i=2;i<=upper;i++)
- {
- //用i除以从2开始、到i的平方根的所有数
- for(int j=2;j<=Math.sqrt(i);j++)
- {
- //如果可以整除,则表明这个数不是质数
- if(i!=2&&i%j==0)
- {
- continue outer;
- }
- }
- nums.add(i);
- }
- //使用Toast显示统计出来的所有质数
- Toast.makeText(CalPrime.this,nums.toString(),Toast.LENGTH_LONG).show();
- }
- }
- };
- Looper.loop();
- }
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_cal_prime);
- etNum=(EditText)findViewById(R.id.etNum);
- calThread=new CalThread();
- //启动新线程
- calThread.start();
- }
- //为按钮的点击事件提供事件处理函数
- public void cal(View source)
- {
- //创建消息
- Message msg=new Message();
- msg.what=0x123;
- Bundle bundle=new Bundle();
- bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
- msg.setData(bundle);
- //向新线程中的Handler发送消息
- calThread.mHandler.sendMessage(msg);
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.cal_prime, menu);
- return true;
- }
- }
上面的粗体字代码是实例的关键代码,这些粗体字代码在新线程内创建了一个Handler,由于在新线程中创建Handler时必须创建Looper,因此程序先调用Looper()的prpare()方法为当前线程创建了一个Looper实例,并创建配套的MessageQueue,新线程有了Looper对象之后,接下来程序创建了一个Handler对象,该Handler可以处理其他线程发送过来的消息。程序还调用了Looper的loop()方法。
运行该程序,无论用户输入多大的数值,计算该范围的质数将会交给新线程完成,而前台UI线程不会受影响。该线程运行效果如图所示。
Handler消息传递机制——Handler、Loop、MessageQueue的工作原理的更多相关文章
- Handler类和Handler,Loop,MessageQueue的工作原理
原文地址:http://blog.csdn.net/xiyangyang8/article/details/50754771 Handler类的作用主要有两种: 1.在新启动的线程中发送消息. 2.在 ...
- Handler消息传递机制——Handler类简洁
Handler类的主要作用有两个: 在新启动的线程中发送消息. 在主线程中获取.处理消息. 上面的说法很简单,只要分成两步即可:在新启动的线程中发送消息:然后在主线程上获取.并处理消息.但这个过程涉及 ...
- 事件处理机制与Handler消息传递机制
一.基于监听的事件处理机制 基于监听的时间处理机制模型: 事件监听机制中由事件源,事件,事件监听器三类对象组成 处理流程如下: Step 1:为某个事件源(组件)设置一个监听器,用于监听用户操作 St ...
- 安卓开发_深入理解Handler消息传递机制
一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存 ...
- Android学习笔记-事件处理之Handler消息传递机制
内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ...
- Handler消息传递机制
引言: 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题. 为了解决这个问题,Android制定了一条简单的规则:只允许UI线程 ...
- 解析Android的 消息传递机制Handler
1. 什么是Handler: Handler 网络释义"机械手.经理"意思,在Android它用于管理多个线程UI操作: 2. 为什么会出现Handler: 在Android里面的 ...
- 3.3 线程---Handler消息传递机制浅析
Handler的执行流程图: 当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueu ...
- Android学习之Handler消息传递机制
Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ...
随机推荐
- hrbustoj 2013 Play Game 2(博弈)
注释在代码里 /* 1.若输入2 ~ 9 ,因为Stan 是先手,所以Stan 必胜 2.若输入10~18 ,因为Ollie 是后手,不管第一次Stan 乘的是什么,Stan肯定在 2 ~ 9 之间, ...
- Unity 之 c# 版的 CharacterMotor
using System; using System.Collections; using UnityEngine; // This class just convert from Character ...
- python之路: 基础篇
)或>>> name = ) #按照占位符的顺序):] #下标识从0开始的 wulaoer >>> print name[:] # ...
- 关于在TabBar 中添加按钮,并通过block 或代理在控制器中实现响应
相信很多朋友会遇到在TabBar中添加按钮,并要求点击按钮能够实现一些功能,但是当我们自定义的时候,怎么才能在控制器中响应?通常我会用代理或者block,block性能更好,建议使用. 自定义TabB ...
- [Lua]Mac系统上安装Lua环境
1.下载 Lua语言的官方网站 http://www.lua.org/ 下载最新版本的Lua环境 2.安装 解压下载包lua-5.3.1.tar.gz 打开终端Terminal 使用cd命令进入该目录 ...
- laravel5 html引用问题
1. Composer 安装 编辑 composer.json 文件, require 节点下增加: "illuminate/html": "~5.0" 然后 ...
- 左偏树初步 bzoj2809 & bzoj4003
看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...
- iOS开发——导入第三方库引起的unknown type name 'NSString'
今天加入SVProgressHUD的第三方库的时候报了24个错误( too many errors emitted, stopping now),都是 expected identifier or ' ...
- javascript--study
1.函数传参:按值传递 对于数字.字符串等是将它们的值传递给了函数参数,函数参数的改变不会影响函数外部的变量. 对于数组和对象等是将对象(数组)的变量的值传递给了函数参数,这个变量保存的指向对象(数组 ...
- jQuery API 中文文档
Reference: http://www.css88.com/jqapi-1.9/jQuery.proxy/