Android消息机制(2)
在Android 中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。
下面我们分析下程序的运行过程:
1.onCreate()
首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。
紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:
- public void run() {
- mTid = Process.myTid();
- Looper.prepare();
- synchronized (this) {
- mLooper = Looper.myLooper();
- notifyAll();
- }
- Process.setThreadPriority(mPriority);
- onLooperPrepared();
- Looper.loop();
- mTid = -1;
- }
在run()方法中,系统给线程添加的Looper,同时调用了Looper的loop()方法:
- public static final void loop() {
- Looper me = myLooper();
- MessageQueue queue = me.mQueue;
- while (true) {
- Message msg = queue.next(); // might block
- //if (!me.mRun) {
- // break;
- //}
- if (msg != null) {
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
- msg.recycle();
- }
- }
- }
通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果 MessageQueue 对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行 msg.target.dispatchMessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到 onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象, 我们把Looper对象传到ServiceHandler构造函数中将使handler、looper和messageQueue三者建立联系。
2.onStartCommand()
执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给 Message对象的arg1、what、obj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用 sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target = this,msg的target指向了this,而this就是ServiceHandler对象,因此msg的target字段指向了 ServiceHandler对象,同时该方法又调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.when != 0) {
- throw new AndroidRuntimeException(msg
- + " This message is already in use.");
- }
- if (msg.target == null && !mQuitAllowed) {
- throw new RuntimeException("Main thread not allowed to quit");
- }
- synchronized (this) {
- if (mQuiting) {
- RuntimeException e = new RuntimeException(
- msg.target + " sending message to a Handler on a dead thread");
- Log.w("MessageQueue", e.getMessage(), e);
- return false;
- } else if (msg.target == null) {
- mQuiting = true;
- }
- msg.when = when;
- //Log.d("MessageQueue", "Enqueing: " + msg);
- Message p = mMessages;
- if (p == null || when == 0 || when < p.when) {
- msg.next = p;
- mMessages = msg;
- this.notify();
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- pprev = p;
- pp = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- this.notify();
- }
- }
- return true;
- }
该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。
handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.enqueueMessage==>把msg添加到消息队列中
3.handleMessage(msg)
onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前 面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对 象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的 dispatchMessage()方法
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。
至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
Android消息机制(2)的更多相关文章
- Android消息机制
每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时 ...
- Android消息机制:Looper,MessageQueue,Message与handler
Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...
- Android消息机制不完全解析(上)
Handler和Message是Android开发者常用的两个API,我一直对于它的内部实现比较好奇,所以用空闲的时间,阅读了一下他们的源码. 相关的Java Class: androi ...
- Android消息机制不完全解析(下)
接着上一篇文章Android消息机制不完全解析(上),接着看C++部分的实现. 首先,看看在/frameworks/base/core/jni/android_os_MessageQueue.cpp文 ...
- Android 消息机制 (Handler、Message、Looper)
综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...
- Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制
本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top2 Crash发生场景:在很平常.频繁的使用页面,打开一个界面,马 ...
- Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...
随机推荐
- 99%Bug 修复方法
以下仅支持4.0.3或之后的设备 (写在前面,这个教程需要安装两个app,且卸载后无法达到效果,所以有app drawer洁癖者慎重(你可以把它们隐藏起来么). 当然等官方rom更新或者安装最新三方r ...
- 了解<hx>标签,为你的网页添加标题
文章的段落用<p>标签,那么文章的标题用什么标签呢?在本节我们将使用<hx>标签来制作文章的标题.标题标签一共有6个,h1.h2.h3.h4.h5.h6分别为一级标题.二级标题 ...
- windows下安装CI框架
CI框架是一个非常流行的 mvc框架, CI框架如何安装和使用,在CI中文网已经讲的比较详细了 ,这里记录下几个需要注意的地方. 一. index.php问题 把压缩包下载解压到项目根目录即可运行里面 ...
- idea maven web工程明明添加了maven lib的依赖,但启动web容器时始终报No Class Found?
idea maven web工程明明添加了maven lib的依赖,但启动web容器时始终报No Class Found? 很久没用idea搭新工程,最近自己想做个东西,冲心搭个web工程,jar包都 ...
- 跟我学android-常用控件之EditText
EditText 是TextView的直接子类,它与TextView的区别在于,EditText可以接受用户输入. 下面通过一个实例来说明EditText的用法 实例:sina 微博的登录界面(注意, ...
- javascript 键值对
<script type="text/javascript"> var arr = new Array(); arr['cn'] = '中国'; arr['usa'] ...
- php把excel数值格式转成日期格式问题
在excel中:40847对应2011-10-31,是日期的数值型表现形式. 在PHP中,echo date('Y-m-d H:i:s',40847);//结果1970-01-01 11:52:30 ...
- js 刷新页面大全
一.先来看一个简单的例子: 下面以三个页面分别命名为frame.html.top.html.bottom.html为例来具体说明如何做. frame.html 由上(top.html)下(bottom ...
- python 列表 字典 读写文件:pickle模块的基本使用
python数据持久存储:pickle模块的基本使用(转载) 作者: pzxbc 出处: http://pzxbc.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保 ...
- getsockname和getpeername函数
这2个函数或者返回与某个套接字关联的本地协议地址(getsockname),或者返回与某个套接字关联的外地协议地址(getpeername) int getsockname(int sockfd, s ...