Handler消息机制分析
来源:互联网 发布:兄弟连mysql视频教程 编辑:程序博客网 时间:2024/05/22 08:22
Handler消息机制分析
标签(空格分隔): Android Handler
概述
- handler允许你发送和处理与MessageQueue消息队列有关的Message和Runnable对象
- 每个hanler实例与一个线程和该线程的队列相联系,它受限于该线程和该消息队列
handler有两种用法:
- 用于将来调度message和执行Runable对象
将一个不同线程的行为放入队列
调度信息方法:post
postAtTime
postDelaysendEmptyMessage
sendMessage
sendMessageAtTime
sendMessageDelayed提醒:post方法允许你放入一个被MessageQueue消息队列调用的Runable对象
sendMessage方法允许将包含一包数据的Message对象放入队列。该Message消息将由Handler的HandlerMessage方法处理(当然你需要实现一个Handler的子类)
当使用post或者send方法的时候
1. 你可以允许他们立即执行(消息队列打算这个干的时候)
2. 指定一个时间延时(即Delay)
3. 指定一个绝对时间(即AtTime)
当一个进程为你的application应用创建的时候,它的主线程从事于运行一个消息队列,该消息队列关注管理顶级水平的一个应用对象(比如activitis,broadcast receiver等)和他们创建的窗口。你可以创建你自己的线程,然后在后台通过Handler与主应用线程交流(这些可以在你的线程中通过前面所说的post,sendMessage方法实现,给定的Message消息和Runnable对象会在handler的消息队列中调度并在适当的时候处理)。
消息处理机制(从发送消息到处理消息)
发送消息
从上可知发送消息主要有send和post方法,我们来看下
/* 将消息加入消息队列,放在所有消息后面 @param msg 参数 @uptimemillis 执行确切时间,精确到微妙(基于SystemClock时间) @return true成功加入队列,false失败(通常是因为处理消息队列的looper推出了)*/public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis);//加入消息队列}
一问:这里我们看见了mQuenue,这是何时创建呢?
我们可以在两个Hanler构造函数中看见
1. public Handler(Callback callback, boolean async) { 当FIND_POTENTIAL_LEAKS设置为真会检测extends Handler的非静态的匿名,本地和成员类。 因为这种类可能会发生内存泄露。 if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//创建队列 mCallback = callback; mAsynchronous = async; } 我们看见是由mLooper创建 2. public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback;//一个接口,里面有一个抽象方法handleMessage(Message msg); mAsynchronous = async;//设置handler是否进行异步处理 } 我们同样可以看见是由looper创建
二问:looper是什么何时创建消息队列
looper概述:
looper用于为一个线程运行消息循环,线程默认是没有与之相关的消息循环的。调用looper的prepare()可以在线程中创建一个消息循环来管理循环。looper的loop操作使之处理消息,知道loop终止。
一个常见的例子是:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare();//初始化当前线程为一个looper mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } };//完成了与looper的绑定。分析如下 Looper.loop();//运行当前线程的消息队列 } }
注:
在主线程中运行的部分,都可以直接使用Handler,因为在主线程启动的过程中(ActivityThread的main函数里)会调用Looper.prepareMainLooper(),Looper类中也直接定义了一个static的looper实例sMainLooper用于存放主线程的Looper,可以通过静态方法获取到。 因此,凡是在主线程中运行的代码段里 都可以直接new Handler()而不用去绑定Looper,MessageQueue;
public static void main(String[] args) { ... Looper.prepareMainLooper();//初始化当前线程作为一个Looper,并将之标记为应用的主Looper. ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
looper与handler绑定过程
public Handler() { this(null, false); } this会使用如下handler。可知looper通过Looper.myLooper在线程中完成绑定 public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper();//返回与当前线程关联的looper对象 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//返回与当前线程关联的消息队列 mCallback = callback; mAsynchronous = async; }
接下来如何在Looper中与线程绑定:
从上面可以知道,在prepare函数中初始化了一个线程为Looper:
public static void prepare() { prepare(true); } 然后调用: 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)); }
sThreadLocal在Looper中的定义
// sThreadLocal.get() will return null unless you’ve called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在此补充ThreadLocal概念:
1. ThreadLocal实现了一个Thread-local仓库,对于每一个线程都有自己的值。
2. 所有线程共享同样的ThreadLocal对象,但是每个线程访问时获取的是不同的值
3. 某个线程对ThreadLocal的变量的修改不会影响其他线程
4. 支持null值
回到正题:Looper是如何创建消息队列
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
由此看出在Looper构造时已经创建
Handler消息如何执行
handler创建后发送的消息就会进入消息队列。此时如果没有为handler单独创建线程,则执行的是ActivityMain的Looper.loop(),否则执行当前线程的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();//重置当前线程IPC(进程间通信)的标识符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.recycleUnchecked();//将消息实例返回到全局池}}
可以看到是msg.target.dispatchMessage(msg)在处理消息
而在Message中定义有这样一个字段:
public final class Message implements Parcelable { ... /*package*/ Handler target; ...}
可知消息正是交给了Handler处理。
我们再看下发送消息过程
在上面sendMessageAtTime的我们可以看见函数enqueueMessage中 msg.target = this;
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//给target赋值为当前handler if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
所以,消息由handler的dispatchMessage分配
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg);//判断msg的callback是否为空,不为空则调用handleCallback } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) {//判断mCallBack是否为空,不为空则调用其handleMessage return; } } handleMessage(msg);//由子类实现 } }
如下,可以看见handleCallback只是调用callback(只是Runnable,一个赋值个Message的线程)
private static void handleCallback(Message message) { message.callback.run(); }
也就是说最后消息就传给了我们自定义的handleMessage;
呼!终于分析完了,不足之处请指正。
- Handler消息机制分析
- Handler消息机制分析
- Handler消息机制源码分析
- 全面分析Handler消息机制
- Android应用程序消息处理机制Handler分析
- android 消息机制 Handler Looper 原理分析
- Handler消息机制的源码分析
- Android消息机制:Looper、Handler、MessageQueue分析
- Handler消息处理机制--原理分析
- Android消息机制 Handler源码分析
- Handler消息机制(深入源码分析)
- Handler消息处理机制源码分析
- [Android] 从源码分析 Handler 消息机制
- Android Handler 消息响应机制源码分析
- 【源码解读】Handler消息机制流程分析
- 消息机制Handler及相关源码分析
- 菜鸟从源码分析Handler消息机制
- Android Handler消息机制源码分析
- 前段博客收藏
- 简单触屏事件
- vmware装64位ubuntu时出现cpu问题
- 找不到#include "stdafx.h"解决办法
- 讯飞语音命令词识别的SDK配置与运用
- Handler消息机制分析
- Java List集合知多少?
- Glib学习(8) 动态字节数组 Byte Arrays
- RecyclerView基本使用方式
- HDU 2135 Rolling table(方阵旋转+找规律)
- 基于树莓派raspberry: 移植 2.4寸TFT显示屏以及源码分析
- Autolayout第三方库Masonry的入门与实践
- dota2 刚被控制台自动切假腿
- 深入浅出 RPC - 浅出篇