全面解析 Android 进阶面试常客之 Handler
Asynchronous messages represent interrupts or events that do not require global ordering
with respect to synchronous messages. Asynchronous messages are not subject to
the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
@param callback The callback interface in which to handle messages, or null.
@param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
@hide
*/
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;
}
通过源码可以看到 Handler 的无参构造函数调用了两个参数的构造函数,而在两个参数的构造函数中就是将一些变量进行赋值。
看下下面的代码
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
这里是通过 Looper 中的 myLooper 方法来获得 Looper 实例的,如果 Looper 为 null 的话就会抛异常,抛出的异常内容翻译过来就是
无法在未调用 Looper.prepare()的线程内创建 handler
从这句话中,我们可以知道,在调用 Looper.myLooper()之前必须要先调用 Looper.prepare()方法,现在来看下 prepare 方法中的内容,如下
/** Initialize the current thread as a looper.
This gives you a chance to create handlers that then reference
this looper, before actually starting the loop. Be sure to call
{@link #loop()} after calling this method, and end it by calling
{@link #quit()}.
*/
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));
}
从上面代码中可以看到,prepare()方法调用了 prepare(boolean quitAllowed)方法,prepare(boolean quitAllowed) 方法中则是实例化了一个 Looper,然后将 Looper 设置进 sThreadLocal 中,到了这里就有必要了解一下 ThreadLocalle。
什么是 ThreadLocal
ThreadLocal 为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
如果看完上面这段话还是搞不明白 ThreadLocal 有什么用,那么可以看下下面代码运行的结果,相信看下结果你就会明白 ThreadLocal 有什么作用了。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Log.d(TAG, "onCreate: "+mThreadLocal.get());
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThreadLocal.set(5);
Thread1 thread1 = new Thread1();
thread1.start();
Thread2 thread2 = new Thread2();
thread2.start();
Thread3 thread3 = new Thread3();
thread3.start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
mHandler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
class Thread1 extends Thread {
@Override
public void run() {
super.run();
mThreadLocal.set(1);
Log.d(TAG, "mThreadLocal1: "+ mThreadLocal.get());
}
}
class Thread2 extends Thread {
@Override
public void run() {
super.run();
mThreadLocal.set(2);
Log.d(TAG, "mThreadLocal2: "+ mThreadLocal.get());
}
}
class Thread3 extends Thread {
@Override
public void run() {
super.run();
mThreadLocal.set(3);
Log.d(TAG, "mThreadLocal3: "+ mThreadLocal.get());
}
}
}
看下这段代码运行之后打印的 log
可以看到虽然在不同的线程中对同一个 mThreadLocal 中的值进行了更改,但最后仍可以正确拿到当前线程中 mThreadLocal 中的值。由此我们可以得出结论 ThreadLocal.set 方法设置的值是与当前线程进行绑定了的。
知道了 ThreadLocal.set 方法的作用,则 Looper.prepare 方法就是将 Looper 与当前线程进行绑定(当前线程就是调用Looper.prepare方法的线程)
。
文章到了这里我们可以知道以下几点信息了
在对 Handler 进行实例化的时候,会对一些变量进行赋值。
对 Looper 进行赋值是通过 Looper.myLooper 方法,但在调用这句代码之前必须已经调用了 Looper.prepare 方法。
Looper.prepare 方法的作用就是将实例化的 Looper 与当前的线程进行绑定。
这里就又出现了一个问题:在调用 Looper.myLooper 方法之前必须必须已经调用了 Looper.prepare 方法,即在实例化 Handler 之前就要调用 Looper.prepare 方法,但是我们平常在主线程中使用 Handler 的时候并没有调用 Looper.prepare 方法呀!这是怎么回事呢?
其实,在主线程中 Android 系统已经帮我们调用了 Looper.prepare 方法,可以看下 ActivityThread 类中的 main 方法,代码如下
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
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.prepareMainLooper();
这句话的实质就是调用了 Looper 的 prepare 方法,代码如下
public static void prepareMainLooper() {
prepare(false);//这里调用了 prepare 方法
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
到这里就解决了,为什么我们在主线程中使用 Handler 之前没有调用 Looper.prepare 方法的问题了。
让我们再回到 Handler 的构造方法中,看下
mLooper = Looper.myLooper();
myLooper()方法中代码如下
/**
Return the Looper object associated with the current thread. Returns
null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
其实就是从当前线程中的 ThreadLocal 中取出 Looper 实例。
再看下 Handler 的构造方法中的
mQueue = mLooper.mQueue;
这句代码。这句代码就是拿到 Looper 中的 mQueue 这个成员变量,然后再赋值给 Handler 中的 mQueue,下面看下 Looper 中的代码
final MessageQueue mQueue;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
同过上面的代码,我们可以知道 mQueue 就是 MessageQueue,在我们调用 Looper.prepare 方法时就将 mQueue 实例化了。
Handler 的 sendMessage 方法都做了什么
还记得文章开始时的两个问题吗?
Handler 明明是在子线程中发的消息怎么会跑到主线程中了呢?
Handler 的发送消息 handleMessage 又是怎么接收到的呢?
下面就分析一下 Handler 的 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);
}
/**
Enqueue a message into the message queue after all pending messages
before the absolute time (in milliseconds) <var>uptimeMillis</var>.
<b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
Time spent in deep sleep will add an additional delay to execution.
You will receive it in {@link #handleMessage}, in the thread attached
to this handler.
@param uptimeMillis The absolute time at which the message should be
@return Returns true if the message was successfully placed in to the
*/
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);
}
由上面的代码可以看出,Handler 的 sendMessage 方法最后调用了 sendMessageAtTime 这个方法,其实,无论时 sendMessage、sendEmptyMessage 等方法最终都是调用 sendMessageAtTime。可以看到 sendMessageAtTime 这个方法最后返回的是_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);
}
这里有一句代码非常重要,
msg.target = this;
这句代码就是将当前的 Handler 赋值给了 Message 中的 target 变量。这样,就将每个调用 sendMessage 方法的 Handler 与 Message 进行了绑定。
enqueueMessage 方法最后返回的是**queue.enqueueMessage(msg, uptimeMillis);**也就是调用了 MessageQueue 中的 enqueueMessage 方法,下面看下 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;
}
上面的代码就是将消息放进消息队列中,如果消息已成功放入消息队列,则返回 true。失败时返回 false,而失败的原因通常是因为处理消息队列正在退出。代码分析到这里可以得出以下两点结论了
Handler 在 sendMessage 时会将自己设置给 Message 的 target 变量即将自己与发送的消息绑定。
Handler 的 sendMessage 是将 Message 放入 MessageQueue 中。
到了这里已经知道 Handler 的 sendMessage 是将消息放进 MessageQueue 中,那么又是怎样从 MessageQueue 中拿到消息的呢?想要知道答案请继续阅读。
怎样从 MessageQueue 中获取 Message
在文章的前面,贴出了 ActivityThread 类中的 main 方法的代码,不知道细心的你有没有注意到,在 main 方法的结尾处调用了一句代码
Looper.loop();
好了,现在可以看看_Looper.loop();_这句代码到底做了什么了 loop 方法中的代码如下
/**
Run the message queue in this thread. Be sure to call
{@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//通过 myLooper 方法拿到与主线程绑定的 Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//从 Looper 中得到 MessageQueue
// 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 mu
st 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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//这句代码是重点
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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);
执行到这句代码,说明已经从消息队列中拿到了消息,还记得 msg.target 吗?就是 Message 中的 target 变量呀!也就是发送消息的那个 Handler,所以这句代码的本质就是调用了 Handler 中的 dispatchMessage(msg)方法,代码分析到这里是不是有点小激动了呢!稳住!下面看下 dispatchMessage(msg)这个方法,代码如下
/**
Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
现在来一句句的来分析上面的代码,先看下这句
if (msg.callback != null) {
handleCallback(msg);
}
msg.callback 就是 Runnable 对象,当 msg.callback 不为 null 时会调用 handleCallback(msg)方法,先来看下 handleCallback(msg)方法,代码如下
private static void handleCallback(Message message) {
message.callback.run();
}
上面的代码就是调用了 Runnable 的 run 方法。那什么情况下**if (msg.callback != null)**这个条件成立呢!还记得使用 Handler 的另一种方法吗?就是调用 Handler 的 post 方法呀!这里说明一下,使用 Handler 其实是有两种方法的
使用 Handler 的 sendMessage 方法,最后在 handleMessage(Message msg)方法中来处理消息。
使用 Handler 的 post 方法,最后在 Runnable 的 run 方法中来处理,代码如下
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mTimeCycle,mStopCycle;
private Runnable mRunnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTimeCycle = findViewById(R.id.btn_time_cycle);
mTimeCycle.setOnClickListener(this);
mStopCycle = findViewById(R.id.btn_stop_cycle);
mStopCycle.setOnClickListener(this);
mRunnable = new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "正在循环!!!", Toast.LENGTH_SHORT).show();
mHandler.postDelayed(mRunnable, 1000);
}
};
评论