Android消息处理机制

Google参考了Windows的消息处理机制,在Android系统中实现了一套类似的消息处理机制。学习Android的消息处理机制,有几个概念(类)必须了解:1.       Message消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。2.       Message Queue消息队列,用来存放通

大家好,又见面了,我是全栈君。

Google参考了Windows的消息处理机制,在Android系统中实现了一套类似的消息处理机制。学习Android的消息处理机制,有几个概念(类)必须了解:

1.       Message

消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。

2.       Message Queue

消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

3.       Handler

Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。

4.       Looper

循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。

Android消息处理机制

 

图1 类关联关系图

 

运行机理:

      每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueue在Looper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的。Android应用在启动的时候会默认会为主线程创建一个Looper实例,并借助相关的Handler和Looper里面的MessageQueue完成对Activities、Services、Broadcase Receivers等的管理。而在子线程中,Looper需要通过显式调用Looper. Prepare()方法进行创建。Prepare方法通过ThreadLocal来保证Looper在线程内的唯一性,如果Looper在线程内已经被创建并且尝试再度创建”Only one Looper may be created per thread”异常将被抛出。

 

      Handler在创建的时候可以指定Looper,这样通过Handler的sendMessage()方法发送出去的消息就会添加到指定Looper里面的MessageQueue里面去。在不指定Looper的情况下,Handler绑定的是创建它的线程的Looper。如果这个线程的Looper不存在,程序将抛出”Can’t create handler inside thread that has not called Looper.prepare()”。

 

      整个消息处理的大概流程是:1. 包装Message对象(指定Handler、回调函数和携带数据等);2. 通过Handler的sendMessage()等类似方法将Message发送出去;3. 在Handler的处理方法里面将Message添加到Handler绑定的Looper的MessageQueue;4. Looper的loop()方法通过循环不断从MessageQueue里面提取Message进行处理,并移除处理完毕的Message;5. 通过调用Message绑定的Handler对象的dispatchMessage()方法完成对消息的处理。

 

      在dispatchMessage()方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:1. Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;2. Handler里面mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;3. 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
    

示例代码:

public class MainThread extends Activity {

	private static final String TAG = "MainThread";
	private Handler mMainHandler, mChildHandler;
	private TextView mInfoTextView;
	private Button mMsgButton;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		mInfoTextView = (TextView) findViewById(R.id.info);
		mMsgButton = (Button) findViewById(R.id.msg);
		/**
		 * 没有指定Looper,Handler将绑定创建它的线程的Looper,这里mMainHandler将绑定主线程的Looper
		 * 
		 * 在主线程中不用显式调用Looper.prepare()创建Looper
		 * 
		 */
		mMainHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				// 接收子线程的消息
				Log.i(TAG, "接收到从子线程发送过来的消息 - " + (String) msg.obj);
				mInfoTextView.setText((String) msg.obj);
			}
		};
		// 创建并启动子线程
		new ChildThread().start();
		mMsgButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mChildHandler != null) {
					// 构建消息对象
					Message childMsg = mChildHandler.obtainMessage();
					childMsg.obj = "我是主线程'"
							+ mMainHandler.getLooper().getThread().getName()
							+ "'";
					// 发送消息给子线程
					mChildHandler.sendMessage(childMsg);
					Log.i(TAG, "发送给子线程的消息 - " + (String) childMsg.obj);
				}
			}
		});

	}

	public void onDestroy() {
		super.onDestroy();
		Log.i(TAG, "Stop looping the child thread's message queue");
		// 关闭子线程的循环器
		mChildHandler.getLooper().quit();
	}

	class ChildThread extends Thread {

		private static final String CHILD_TAG = "ChildThread";

		public void run() {
			this.setName("ChildThread");
			/**
			 * 初始化消息队列,如果在子线程里创建Handler没有执行Looper.prepare()将报如下错误
			 * 
			 * Can't create handler inside thread that has not called
			 * Looper.prepare()
			 */
			Looper.prepare();
			mChildHandler = new Handler() {
				@Override
				public void handleMessage(Message msg) {
					Log.i(CHILD_TAG, "接收到从主线程发送过来的消息  - " + (String) msg.obj);
					try {
						// 在子线程中可以做一些耗时的工作
						sleep(100);
						// 构建消息对象
						Message toMain = mMainHandler.obtainMessage();
						toMain.obj = "我是子线程'"
								+ this.getLooper().getThread().getName() + "'";
						// 发送消息给主线程
						mMainHandler.sendMessage(toMain);
						Log.i(CHILD_TAG, "发送给总线程的消息 - " + (String) toMain.obj);
					} catch (InterruptedException e) {
						Log.e(TAG, e.getMessage());
					}
				}
			};
			// 启动子线程消息循环队列,开始处理消息
			Looper.loop();
		}
	}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/121479.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 京东云服务器免费体验_京东云服务器免费体验

    京东云服务器免费体验_京东云服务器免费体验1元体验京东云服务器在大神鸿洋和郭霖的微信公众号推送文章发现了这个好消息,第一时间注册了账号,申请下了订单,毕竟数量有限。尽管两位大神讲的很详细,无奈没有接错过CentOS、Ubuntu、WindowsServer这些服务器系统的小白,还是有些地方不是很清楚,正所谓,打破砂锅问到底,不仅要知其然还要知其所以然;经过一段时间的琢磨。还是搞明白了。CentOS和Ubuntu的相同与不同CentO

    2022年10月14日
    0
  • PhalApi 1.4.2 经典封存版 – 码云

    PhalApi 1.4.2 经典封存版 – 码云

    2021年11月4日
    40
  • 移动端分享插件使用总结

    移动端分享插件使用总结

    2021年10月28日
    54
  • VS Code关闭eslint校验

    VS Code关闭eslint校验一、产生原因:在编写vue代码的时候,一直因为格式问题报错,按照要求改了格式,虽不报错,但当选择格式化文档,就会再次报错,所以需要关闭格式校验功能。二、解决办法:①:若报错,可将鼠标放在报错位置,按照提示内容,单机右键,选择快速恢复;但后期影响继续存在②:关闭校验功能步骤:1.点击左下角的设置图标并选择设置2.搜索eslint,如图并勾选可取消报错:3.重启VSCode,编译时不再报错…

    2022年6月11日
    65
  • zabbix 监控介绍「建议收藏」

    zabbix 监控介绍「建议收藏」一、监控介绍你用过哪些监控软件?zabbix和nagios、cacti、ganglia有什么区别?zabbix有那些好处?zabbix的监控流程是什么?zabbix常见监控项有那些?1、CactiCacti是一套基于PHP、MySQL、SNMP及RRDTool开发的监测图形分析工具,Cacti是使用轮询的方式由主服务器向设备发送数据请求来获取设备上状态数据信息的,如果设备不断增多,这个轮询的过程就非常的耗时,轮询的结果就不能即时的反应设备的状态

    2022年4月28日
    77
  • java中遍历数组的方法_java遍历object数组

    java中遍历数组的方法_java遍历object数组参考labmbda表达式引言记录一下Java遍历数组的几种常见方法下面以遍历整数数组为例Integer[]arr={1,3,4,5,6};//这里不能使用int[],会报错//!!!注意:使用Arrays.asList转换为集合时,不能用其进行修改集合的相关方法(add/remove)List<Integer>list=Arrays.asList(arr);1、利用for遍历//1、利用for遍历System.out.prin

    2022年9月19日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号