写点什么

全面解析 Android 进阶面试常客之 Handler

用户头像
Android架构
关注
发布于: 1 小时前
  • 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,而失败的原因通常是因为处理消息队列正在退出。代码分析到这里可以得出以下两点结论了


  1. Handler 在 sendMessage 时会将自己设置给 Message 的 target 变量即将自己与发送的消息绑定。

  2. 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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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 其实是有两种方法的


  1. 使用 Handler 的 sendMessage 方法,最后在 handleMessage(Message msg)方法中来处理消息。

  2. 使用 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);


}


};

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
全面解析Android进阶面试常客之Handler