ThreadLocal

往下看之前,需要了解一下Java的ThreadLocal类,可参考博文:

解密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的更多相关文章

  1. Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息

    先看简单示例:点击按钮,2s之后,TextView改变内容. package cn.lixyz.handlertest; import android.app.Activity; import and ...

  2. Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解

    我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.handlertest; ...

  3. Android笔记(三十二) Android中线程之间的通信(四)主线程给子线程发送消息

    之前的例子都是我们在子线程(WorkerThread)当中处理并发送消息,然后在主线程(UI线程)中获取消息并修改UI,那么可以不可以在由主线程发送消息,子线程接收呢?我们按照之前的思路写一下代码: ...

  4. Android笔记(三十) Android中线程之间的通信(二)Handler消息传递机制

    什么是Handler 之前说过了,Android不允许主线程(MainThread)外的线程(WorkerThread)去修改UI组件,但是又不能把所有的更新UI的操作都放在主线程中去(会造成ANR) ...

  5. Android笔记三十三.BroadcastReceiver使用

        广播是一种广泛运用在应用程序之间传输信息的机制,而BroadcastReceiver是对发送出来的广播进行过滤接收并响应的一类组件. BroadcastReceiver本质上是一种全局监听器. ...

  6. Android笔记(六十三) android中的动画——逐帧动画( frame-by-frame animation)

    就好像演电影一样,播放实现准备好的图片,来实现动画效果. 逐帧动画需要用到AnimationDrawable类,该类主要用于创建一个逐帧动画,然后我们把这个动画设置为view的背景即可. androi ...

  7. Android笔记(七十三) Android权限问题整理 非常全面

    Android权限系统非常庞大,我们在Android系统中做任何操作都需要首先获取Android系统权限,本文记录了所有的Android权限问题,整理一下分享给大家. 访问登记属性 android.p ...

  8. Java多线程中线程间的通信

    一.使用while方式来实现线程之间的通信 package com.ietree.multithread.sync; import java.util.ArrayList; import java.u ...

  9. java线程之间的通信

    1.常用的方法 sleep() 该线程进入等待状态,不释放锁 wait() 该线程进入等待状态,释放锁 notify() 随机唤醒一个线程 notifyAll() 唤醒全部线程 getName() 获 ...

随机推荐

  1. 123457123456#2#----com.MC.HuiHuaGame33--前拼后广--画画填色Game-mc

    com.MC.HuiHuaGame33--前拼后广--画画填色Game-mc

  2. k8s、jenkins集成

    参考连接 http://www.uml.org.cn/jchgj/201804252.asp https://huanqiang.wang/2018/03/30/Jenkins-Gitlab-Kube ...

  3. Linux脚本检测当前用户是否为root用户

    #/bin/bash if [ $UID -ne 0 ]; then echo Non root user. Please run as root. else echo Root user fi

  4. LODOP打印table不切行TableRowThickNess

    不切行的调整方法有三种:1.简单表格,不嵌套合并等,可以用ADD_PRINT_TABLE输出,该 语句不切行,相关博文:LODOP设置超文本不自动分页的方法.2.进入打印设计,调整打印项到合适的高度, ...

  5. 【VxWorks工程】基于opencv创建读取摄像头数据的工程error

    工程编译遇到的问题: Cannot step: Signal : Segmentation violation bytes (0x20 aligned) in partition 0x2023d930 ...

  6. 页面进行ajax时 显示一个中间浮动loading

    先发效果图,加载东西的时候如果没有设计或者其它提示会降低用户体验,所以写了个简单的loading弹层. 适用于触屏和pc页面. /* 页面进行ajax时 显示一个中间浮动loading @auther ...

  7. iOS-退出App程序,进入设置页面

     AppDelegate *app = [UIApplication sharedApplication].delegate;     UIWindow *window = app.window;   ...

  8. Android StickyListHeadersListView头部悬停 分组列表实现

    最近在做分组列表,头部悬停的效果,在github上就搜到了StickyListHeaders(https://github.com/emilsjolander/StickyListHeaders)这个 ...

  9. python selenium学习

    selenium是一个python模块,具有自动化模拟浏览器的功能 selenium的中文文档地址:http://selenium-python-zh.readthedocs.io/en/latest ...

  10. 【转帖】如果进入CPU的世界,时间会是怎样的?

    如果进入CPU的世界,时间会是怎样的? 2018-02-26 20:52:46 world6 阅读数 1295更多 分类专栏: 网络 缓存服务 架构   版权声明:本文为博主原创文章,遵循CC 4.0 ...