【1】Looper如何和Thread联系起来?
答:以主线程为例解释:
在ActivityThread类中的程序的入口,即main方法,该方法中调用了:
Looper.prepareMainLooper();
Step:接下来我们解析Looper类中的该方法:
public static void prepareMainLooper() {
prepare(false); synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared."); }
sMainLooper = myLooper(); }
}
官方对该方法的解释:
(1)将当前线程初始化为looper,将其标记为应用程序的主looper。
(2)应用程序的主looper是由Android环境创建的,所以你应该永远不要自己调用该方法。
Step:接下来我们解析:
prepare(false);
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));}
官方对该方法的解释:
(1)该方法让你有机会创建handlers,然后引用这个looper,然后才开始真正循环(loop)。
(2)调用此方法后一定要调用loop(),通过调用quit()来结束它。
Step:接下来我们解析:sThreadLocal.set(new Looper(quitAllowed));
(1)首先我们关注创建Looper对象:Looper looper = new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
这里主要是:
(1)创建了MessageQueue的对象mQueue;
(2)创建了Thread的对象mThread;
Thread.currentThread()方法可以获取到当前的线程,当应用程序启动的时候,系统会为该应用程序创建一个线程,我们叫它主线程。
(2)其次我们关注Looper对象的存储:sThreadLocal.set(looper)
我们看看sThreadLocal是什么东东?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
官方对ThreadLocal的解释:
用来实现线程本地存储,即每个线程的变量有自己对应的值。
所有的线程共享相同的ThreadLocal对象,但是每个线程访问它时都会看到不同的值,并且有一个线程更改不会影响其他线程。
Step:接下来我们解析:set(looper)
public void set(T value) {
Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) {
values = initializeValues(currentThread); }
values.put(this, value);}
官方对该方法的解释:
为当前线程设置此变量的值。
这里不对ThreadLocal类进行深入的了解,到这里我们知道使用该类可以实现存储Thread和Looper就可以了,类似于(key-value)。
因为Thread默认没有与它关联的消息循环,(Thread默认不能进行消息循环)
要创建一个,我们在运行循环的线程中调用prepare(),
然后调用loop()方法让他处理消息,
最后调用quit()方法推出循环
Looper最重要的方法以及顺序:prepare() -> loop() ->quit()
实际开发中的使用:参考HandlerThread
【1】extends Thread
【2】Looper.prepare() //将Thread初始化为looper
【3】创建一个或者多个Handler,用来处理message
【4】Looper.loop() //分发message,直到loop被告知quit()
【2】Handler如何和Thread联系起来?
主要从分析Handler源码来解析:
Step:先看默认构造函数:
public Handler() {
this(null, false);}
官方对该构造函数的解释:
默认构造函数将该Handler与当前线程的Looper关联起来。如果此线程没有looper,该Handler将无法接收Message,因此会抛出异常。
Step:再看看另一个构造函数:
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;}
官方对该构造函数的解释:
对于具有指定的回调接口的当前Thread使用Looper,并设置该Handler是否应该是异步的。
Handler默认情况下是同步的,除非此构造函数用于创建严格异步的。
mLooper:在Looper中通过prepare()方法创建的,这样Handler就和Looper联系起来了,【同时和Thread联系起来。】
mQueue:Handler中的MessageQueue和Looper中的MessageQueue是一致的。
Step:接着看Handler的两个主要用途:
【1】调度message 和 runnable ,在未来的某个点执行。
【2】在与自己不同的线程上执行某个事件。
调度消息可以通过如下方法完成,大致有两类型:post 和 send
post方式允许在接收到消息时将Runnable对象入队;
send方式允许在接收到消息时将Message对象入队;
Step:关于post方式做如下说明:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);}
官方解释:
将Runnable添加到MessageQueue.Runnable将在Handler所在的Thread中运行。
Step:再看看getPostMessage(r):
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain(); m.callback = r; return m;}
将Runnable用Message包裹起来,以Message的形式发送出去。
记住:无论是post方式,还是send方式,最终都只调用一个方法:
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);}
官方解释:
将Message置入MessageQueue中。(这里时间先不做解释了)
Step:再看看enqueueMessage()方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; if (mAsynchronous) {
msg.setAsynchronous(true); }
return queue.enqueueMessage(msg, uptimeMillis);}
重点:msg.target = this;这是非常重要的,因为Looper中的loop() 方法会用到它,即:msg.target.dispatchMessage(msg);
目的是要求发送Message的Handler和处理Message的Handler是一致的。
Step:接下来重点就是MessageQueue的enqueueMessage()方法,
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target."); }
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use."); }
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; }
msg.markInUse(); 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;}
至此,Message已经放入MessageQueue中了;
Step:接下来就是从MessageQueue中取Message了,这时候就需要发挥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 final Printer logging = me.mLogging; if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what); }
final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); }
try {
msg.target.dispatchMessage(msg); } finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag); }
}
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)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); } else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; }
}
handleMessage(msg); }
}
这里我们看到它分情况处理了,msg.callback是否为null。
如果msg.callback不为null,说明传递过来的是Runnable,然后进行handleCallback(msg)
private static void handleCallback(Message message) {
message.callback.run();}
如果msg.callback为null,说明传递过来的是Message,然后进行handleMessage(msg)
public void handleMessage(Message msg) {
}
我们需要在run()方法和handleMessage()方法中写自己的实现。
【3】通过对【1】和【2】的分析,我发现:
我们主要了解Thread,Looper,Handler它们三个之间的关系就可以了,Message和MessageQueue可以把它们当作媒介就可以。
出场顺序是这样的:Thread -> Looper -> Handler
Thread创建后,就需要对Looper进行操作,主要是执行两个方法:prepare(),loop(), 这是准备工作。
然后就是对Handler的使用,创建Handler对象,并将其和Looper联系起来,并和Thread联系起来,
Handler就可以发挥它强大的功能。
【4】思考:
UI线程是如何创建的?
应用启动时,系统会为应用创建一个名为主线程的执行线程。主线程负责将事件分派给相应的用户界面小部件,其中包括绘图事件。
此外,UI线程也是应用与Android UI工具包组件进行交互的线程。
UI线程才能处理UI相关的操作,为什么?
答:Android UI工具包不是线程安全的。因此,单线程模型确保UI不能被不同的线程同时修改。
Android关于线程的详细说明:https://developer.android.com/guide/components/processes-and-threads.html
【5】Handler的工作原理:
Handler创建时会采用当前线程的Looper来构建内部的消息循环系统。接下来看Handler的运行机制
【6】ThreadLocal:
在不同的线程中访问的是同一个ThreadLocal对象,但是他们通过ThreadLocal获取到的值是不同的。
why? ->不同线程访问同一个ThreadLocal的get()方法,ThreadLocal内部会从各自的线程中取出一个数组,
网友评论