android学习-异步消息处理机制
消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable)
Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠。
这些类的主要变量
Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue;
final Thread mThread;
Handler.java
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
Message.java
Handler target;每个消息只能对应一个handler
Runnable callback;回调接口
MessageQueue.java
Message mMessages;
Runnable是一个空接口类,没有变量
上一个书上的图:
Handler和Thread没有直接关系,但对应关系可以推理得到
每个Thread只对应一个Looper;
每个Looper只对应一个MessageQueue;
每个MessageQueue对应N个Message,每个Message只对应一个Handler
==》每个Thread对应N个Handler。
Handler是”真正处理事情“的地方,作用:处理消息,将Message压入MessageQueue中
带着一个问题看源码:创建handler对象的线程(ui/主线程除外)为什么,必须先调用Looper.prepare() ?
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
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) == ) {
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
初始化handler对象时(构造方法是Handler(),Handler(Callback callback))都间接调用Handler(Callback callback, boolean async)构造方法
主要代码是Looper.myLooper();
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//这是在Looper类中的定义
public static Looper myLooper() {
return sThreadLocal.get();//从当前线程中获得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));//为当前线程设置looper对象
}
我们自己创建线程必须通过Looper.prepare()方法为当前线程设置looper对象才可以通过Looper.myLooper()方法返回looper对象,这样在非UI线程创建handler对象时才不会报错。"Can't create handler inside thread that has not called Looper.prepare()"
ps:prepare(boolean quitAllowed)(这个不用我们关心,略过。。)
这个quitAlowed参数是定义消息队列用了,看的源代码是android4.4
Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
MessageQueue.java
// True if the message queue can be quit.
private final boolean mQuitAllowed;//true消息队列可以被quit,false消息队列不能被quit。
主线程/UI线程的MessageQueue不能被销毁掉。看源码(销毁调用Looper.quit())
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
偏离太远了
所以得出结论:创建handler对象的线程(ui/主线程除外),必须先调用Looper.prepare()
Handler作用1:处理消息
在Looper类中处理消息是通过msg.target.dispatchMessage(msg);target就是handler对象(Message类的内部变量Handler target)将消息转发到处理消息的对应的handler对象上,然后这个target即handler对象会在处理消息前做一个检查
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果msg有绑定callback回调接口Runaable不为空,则执行Runnable的run方法
handleCallback(msg);
} else {
if (mCallback != null) {//如果handler的内置接口类Callback不为空,则执行boolean handleMessage(Message msg)这个方法
if (mCallback.handleMessage(msg)) {执行完成则return
return;
}
}
handleMessage(msg);//最后才执行handler本身的方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public interface Callback {//handler的内置接口类Callback
public boolean handleMessage(Message msg);
}
Handler作用2:将Message压入MessageQueue中
handler中提供的很多发送message的方法,除了sendMessageAtFrontOfQueue()方法(直接调用enqueueMessage(queue, msg, 0);)之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法
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);
}
sendMessageAtTime()方法也是调用Handler中的enqueueMessage(queue, msg, uptimeMillis)方法
和sendMessageAtFrontOfQueue()方法两者最后都会调用enqueueMessage(queue, msg, uptimeMillis)方法
区别是需要延迟uptimeMillis时间后才将Message压入MessageQueue中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//给msg的target赋值为handler自身然后加入MessageQueue中
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终所有的方法都是调用MessageQueue中的enqueueMessage(msg, uptimeMillis);方法,是不是感觉两个方法差不多啊,注意参数!!
MessageQueue的使用是在Looper中
Handler的作用整理完毕(好像我现在已经可以把Handler源码完整默写下来了。哈哈^.^记忆力真不行)
Looper类
作用:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
对于Looper主要是prepare()和loop()两个方法
prepare()将普通线程转化为looper线程,
loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
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();
}
}
27行就是上面提到了,handler进行消息处理的关键代码了
看着上面的分析很复杂,总结下
1、首先Looper.prepare()为在当前线程中保存一个Looper实例(sThreadLocal.set()),然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
这篇博客很不错,借鉴了
http://blog.csdn.net/lmj623565791/article/details/38476887
android学习-异步消息处理机制的更多相关文章
- Android多线程----异步消息处理机制之Handler详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类(转)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号: ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片载入类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...
- Android学习之异步消息处理机制
•前言 我们在开发 APP 的过程中,经常需要更新 UI: 但是 Android 的 UI 线程是不安全的: 如果想更新 UI 线程,必须在进程的主线程中: 这里我们引用了异步消息处理机制来解决之一问 ...
- 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...
- Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...
- Android线程之异步消息处理机制(三)——AsyncTask
Android的异步消息处理机制能够很完美的解决了在子线程中进行UI操作的问题,但是为了更加方便我们在子线程中对UI进行操作,Android还提供了另一个很好用的工具,AsyncTask就是其中之一. ...
- Android Learning:多线程与异步消息处理机制
在最近学习Android项目源码的过程中,遇到了很多多线程以及异步消息处理的机制.由于之前对这块的知识只是浅尝辄止,并没有系统的理解.但是工程中反复出现让我意识到这个知识的重要性.所以我整理出这篇博客 ...
- Android异步消息处理机制
安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...
随机推荐
- (连通图 模板题 出度和入度)Network of Schools--POJ--1236
链接: http://poj.org/problem?id=1236 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82833#probl ...
- android 热更新nuwa
简介 Nuwa是比较流行的一种Android热补丁方案的开源实现,它的特点是成功率高,实现简单.当然,热补丁的方案目前已经有很多了,AndFix, Dexposed, Tinker等,之所以要分析Nu ...
- java 把InputStream流写入到文件中
基于流(Stream)的解决 流是单向的有方向性的描述信息流的对象,InputStream是输入流的接口,对程序来说是入,是读,可以从文件读,缓存区读,网络节点读等等. 写入文件,对程序来说是出,是写 ...
- C# 读取Excel表格内容,以及NPOI的使用
在实际的开发中,我们可能需要读写word或者Excel的内容,在我开发的项目中,需要读取Excel的内容,并将相对应的内容存储到数据库中,这里简单跟大家分享一下,希望能够帮助一些人. 我相信在读写wo ...
- Android - "已安装了存在签名冲突的同名数据包",解决方法!
错误提示:已安装了存在签名冲突的同名数据包. 解决方法:打开Android Studio,打开logcat,用usb线连接你出错的手机,识别出手机之后,在你的项目后面,点击“run”按钮,随后AS会提 ...
- redis惊群
本文链接:http://www.cnblogs.com/zhenghongxin/p/8681168.html 什么是惊群 首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了 ...
- oh-my-zsh 安装及使用
什么是 oh-my-zsh oh-my-zsh 是基于 zsh 的功能做了一个扩展,方便的插件管理.主题自定义,以及漂亮的自动完成效果. oh-my-zsh 安装及插件安装 查看系统中 zsh 及版本 ...
- linux下程序编译出错解决方法
一 *** No rule to make target 'subscribe_pkg/CMakeFiles/subscribe_pkg_node.dir/build'. 停止.CMakeFiles/ ...
- Codeforces Round #427 (Div. 2) B. The number on the board
引子: A题过于简单导致不敢提交,拖拖拉拉10多分钟还是决定交,太冲动交错了CE一发,我就知道又要错过一次涨分的机会.... B题还是过了,根据题意目测数组大小开1e5,居然蒙对,感觉用vector更 ...
- c++之sleep函数
c++之sleep函数 c++中使用sleep函数需要导入第三方库,标准库中没有该函数实现. 我们导入window.h使用Sleep()方法,注意:第一个S要大写,括号中的表示的整数倍的毫秒 Slee ...