Android笔记(三十三) Android中线程之间的通信(五)Thread、Handle、Looper和MessageQueue
ThreadLocal
往下看之前,需要了解一下Java的ThreadLocal类,可参考博文:
Looper、Handler和MessageQueue
我们分析一下之前的这段代码,查看一下Handler机制中,Handle、Looper和MessageQueue之间到底是什么关系
package cn.lixyz.handlertest; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button; public class MainActivity extends Activity { private Button button;
private Handler handler; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage();
msg.what = 100;
handler.sendMessage(msg);
Log.d("TTTT", "sendMessage:" + Thread.currentThread().getName());
}
}); WorkerThread wt = new WorkerThread();
wt.start();
} class WorkerThread extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("TTTT", "handleMessage:" + Thread.currentThread().getName());
int i = msg.what;
Log.d("TTTT", "收到了消息对象:" + i);
}
};
Looper.loop();
}
}
}
我们分析一下之前的这段代码,查看一下Handler机制中,Handle、Looper和MessageQueue之间到底是什么关系
1. WorkerThread wt = new WorkerThread(); wt.start();
创建并启动线程
2. Looper.prepare();
查看Looper的源代码,位置是:sources\android-19\android\os\Looper.java
首先,Looper类的几个成员变量
private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue;
final Thread mThread; private Printer mLogging;
当我们调用Looper的prepare()方法的时候,执行如下代码:
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.prepare() 之后,会先调用ThreadLocal的 get() 方法,判断是否已经存在以当前线程为键的键值对
如果已经存在,则抛出异常 Only one Looper may be created per thread
如果不存在,则 new Looper(quitAllowed) 创建安Looper对象并使用 set() 方法存储到ThreadLocal中。
如何创建的Looper对象呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
新建了一个MessageQueue对象赋值给Looper对象的mQueue属性,将当前的线程对象赋值给Looper对象的mThread属性。
再看如何创建的MessageQueue对象呢?查看MessageQueue的源代码,位置是:sources\android-19\android\os\MessageQueue.java
先看MessageQueue的属性
// True if the message queue can be quit.
private final boolean mQuitAllowed; @SuppressWarnings("unused")
private int mPtr; // used by native code Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked; // The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
然后找到 MessageQueue(quitAllowed) 方法:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
在MessageQueue方法中,将一个boolean值传递给MessageQueue对象的mQuitAllowed对象,调用 nativeInit() 方法传递给MessageQueue对象的 mPtr 属性,再看nativeInit方法:
private native static int nativeInit();
可见这个方法是需要用户自己去实现的,这里不深入。
至此,一个Looper对象创建出来,然后以当前线程对象为键,该Looper对象为值存入到 ThreadLocal 中,这个Looper对象的属性包含当前的线程对象和一个MessageQueue对象。
接下来开始执行
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int i = msg.what;
}
};
在这段代码里,创建了一个Handler对象,我们查看Handler的代码,位置是:sources\android-19\android\os\Hnadler.java先看这个类的属性:
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
接着看它的构造方法:
public Handler() {
this(null, false);
} 。。。。。。 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();
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.myLooper() ,反过去再看Looper的代码,找到 myLooper() 方法:
public static Looper myLooper() {
return sThreadLocal.get();
}
发现 myLooper() 方法的作用是从ThreadLocal当中以当前线程为键获取到一个Looper对象,然后返回出去,而在我们刚才执行 prepare() 的时候,已经创建了一个Looper对象,所以在这里是将这个对象拿到并且赋值给Handler对象的mLooper属性,拿到这个Looper并且赋值之后,会进行判断这个对象是否为空,如果是空的,会抛出异常 Can't create handler inside thread that has not called Looper.prepare() 来提示用户在创建Handler对象之前,需要先调用Looper的 prepare() 方法。
接下来,将mLooper的mQueue属性赋值给了Handler对象的mQueue属性。
至此,Handler对象也创建出来了,这个对象包含了ThreadLocal中Looper对象的消息队列(MessageQueue)和Looper对象
接下来开始执行
Looper.loop();
查看Looper的代码:
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();
}
}
final Looper me = myLooper(); 先拿到了存在ThreadLocal中的Looper对象,如果这个对象为空,则提示用户没用Looper对象,需要先调用 Looper.prepare() 方法。 final MessageQueue queue = me.mQueue; 拿到这个对象的消息队列,然后开始一个死循环。
通过MessageQueue的源代码可以看到 next() 的作用是从消息队列中取出消息对象并返回,在这里,如果消息队列为空,则会进入阻塞状态 might block 。
因为我们才刚开始运行,所以消息队列中还是空的,进入阻塞状态,至此,准备工作已经完成。
我们点击按钮。
public void onClick(View v) {
Message msg = handler.obtainMessage();
msg.what = 100;
handler.sendMessage(msg);
}
调用handler的 obtainMessage() 方法,我们去查看源代码:
public final Message obtainMessage()
{
return Message.obtain(this);
}
发现其调用了Message的 obtain() 方法,查看Message的源代码,路径:sources\android-19\android\os\Message.java
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
} ...... public static Message obtain(Handler h) {
Message m = obtain();
m.target = h; return m;
}
发现 obtain() 方法显示在消息池中判断有没有消息对象,如果有,则返回这个消息对象,如果没有,则创建一个消息对象并返回。这个消息对象包含 what 、 arg1 、 arg2 、 obj 、 flags 、 when 、 data 、 target 属性
调用handler的 obtainMessage() 方法得到一个Message对象,然后我们给这个Message对象的what属性赋值:
msg.what = 100;
然后将这个消息发送出去:
handler.sendMessage(msg);
我们查看sendMessage()方法
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
} 。。。。。。 public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
} 。。。 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);
} 。。。 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
发现其调用了MessageQueue的 enqueueMessage 方法,接着看
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
} synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
} // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
简而言之,这里判断是否将消息发送出去,也就是消息是否已经加入到消息队列中去
至此,消息已经发出,消息队列中也有消息,那么 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();
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();
}
}
循环体内, queue.next() 得到消息对象之后,调用了 msg.target.dispatchMessage(msg); ,因为Message的target属性是Handler类型,我们查看Handler的 dispatchMessage 方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
发现其调用了 handleMessage 方法
public void handleMessage(Message msg) {
}
而我们又重写了handleMessage方法完成了我们都逻辑,至此,Handler消息传递完成。
本身水平有限,写到这里,已经累够呛,所以文中肯定会有差错的地方,如果您恰好看到了这篇文章发现了错误,麻烦请您告诉我一下,谢谢~~~
Android笔记(三十三) Android中线程之间的通信(五)Thread、Handle、Looper和MessageQueue的更多相关文章
- Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息
先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...
- Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解
我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.handlertest; ...
- Android笔记(三十二) Android中线程之间的通信(四)主线程给子线程发送消息
之前的例子都是我们在子线程(WorkerThread)当中处理并发送消息,然后在主线程(UI线程)中获取消息并修改UI,那么可以不可以在由主线程发送消息,子线程接收呢?我们按照之前的思路写一下代码: ...
- Android笔记(三十) Android中线程之间的通信(二)Handler消息传递机制
什么是Handler 之前说过了,Android不允许主线程(MainThread)外的线程(WorkerThread)去修改UI组件,但是又不能把所有的更新UI的操作都放在主线程中去(会造成ANR) ...
- Android笔记三十三.BroadcastReceiver使用
广播是一种广泛运用在应用程序之间传输信息的机制,而BroadcastReceiver是对发送出来的广播进行过滤接收并响应的一类组件. BroadcastReceiver本质上是一种全局监听器. ...
- Android笔记(六十三) android中的动画——逐帧动画( frame-by-frame animation)
就好像演电影一样,播放实现准备好的图片,来实现动画效果. 逐帧动画需要用到AnimationDrawable类,该类主要用于创建一个逐帧动画,然后我们把这个动画设置为view的背景即可. androi ...
- Android笔记(七十三) Android权限问题整理 非常全面
Android权限系统非常庞大,我们在Android系统中做任何操作都需要首先获取Android系统权限,本文记录了所有的Android权限问题,整理一下分享给大家. 访问登记属性 android.p ...
- Java多线程中线程间的通信
一.使用while方式来实现线程之间的通信 package com.ietree.multithread.sync; import java.util.ArrayList; import java.u ...
- java线程之间的通信
1.常用的方法 sleep() 该线程进入等待状态,不释放锁 wait() 该线程进入等待状态,释放锁 notify() 随机唤醒一个线程 notifyAll() 唤醒全部线程 getName() 获 ...
随机推荐
- java 特殊字符处理
// 去除富文本中的html标签 // <p>段落替换为换行 content = content.replaceAll("<p .*?>", "\ ...
- JAVA协程 纤程 与Quasar 框架
ava使用的是系统级线程,也就是说,每次调用new Thread(....).run(),都会在系统层面建立一个新的线程,然鹅新建线程的开销是很大的(每个线程默认情况下会占用1MB的内存空间,当然你愿 ...
- POJ Corn Fields 状态压缩DP基础题
题目链接:http://poj.org/problem?id=3254 题目大意(名称什么的可能不一样,不过表达的意思还是一样的): 种玉米 王小二从小学一年级到现在每次考试都是班级倒数第一名,他的爸 ...
- 【opencv基础-VxWorks】话说图像格式转换-COLOR_YUV2BGR_YUY2
前言 基于Vxworks的WindRiver获取摄像头图像进行处理,需要先进行转换,对于转换格式博主有点疑问.本文对此作出解释,若有错误,请交流指正. README.md The video came ...
- windows下 使用ip地址反查主机名的命令
nbtstat -a [远端或局域网某计算机的IP地址] 可以查询返回远端或局域网某计算机的主机名 C:\Documents and Settings\z00121596>nbtstat -a ...
- 【GStreamer开发】GStreamer基础教程10——GStreamer工具
目标 GStreamer提供了一系列方便使用的工具.这篇教程里不牵涉任何代码,但还是会讲一些有用的内容: 如何在命令行下建立一个pipeline--完全不使用C 如何找出一个element的Capab ...
- 最新 中细软java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.中细软等10家互联网公司的校招Offer,因为某些自身原因最终选择了 中细软.6.7月主要是做系统复习.项目复盘.LeetC ...
- HDU 4352:XHXJ's LIS
题目:(原题是英文而且很迷) 求区间内数的LIS长度==k的个数,比如153948的LIS为1 3 4 8,长度为4.据说这种题叫DP中DP,本来是线性,再套一层状压+数位,简直厉害到不行…… 线性的 ...
- Tuner工作原理详解
1.TV自动搜台原理:https://wenku.baidu.com/view/3b771f8b84868762caaed514 2.彩电自动搜台的原理与维修:http://tv.baoxiu.c ...
- 串的两种模式匹配方式(BF/KMP算法)
前言 串,又称作字符串,它是由0个或者多个字符所组成的有限序列,串同样可以采用顺序存储和链式存储两种方式进行存储,在主串中查找定位子串问题(模式匹配)是串中最重要的操作之一,而不同的算法实现有着不同的 ...