面試被問懵?帶你一步一步深刻Handler源碼,不信還拿不下面試官?

Handler機制是Android中至關經典的異步消息機制,在Android發展的歷史長河中扮演着很重要的角色,不管是咱們直接面對的應用層仍是FrameWork層,使用的場景仍是至關的多。java

不少朋友面試時問到了這裏,一時被問懵。從哪裏跌倒就從哪裏爬起來,帶你們一步一步深刻Handler源碼,就不信還拿不下面試官!git

BATJ、字節跳動面試專題,算法專題,高端技術專題,混合開發專題,java面試專題,Android,Java小知識,到性能優化.線程.View.OpenCV.NDK等已經上傳到了的個人GitHub
你們點擊個人GitHub地址:https://github.com/Meng997998/AndroidJX點下star一塊兒學習github

分析源碼一探究竟。面試

從一個常見的用法提及:算法

private Button mBtnTest;
private Handler mTestHandler = new Handler(){
   @Override
   public void handleMessage(Message msg) {
       switch (msg.what){
           case 1:
               mBtnTest.setText("收到消息1");
       }
   }
};
@Override
protected void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mBtnTest = (Button) findViewById(R.id.btn_test);
   new Thread(new Runnable() {
       @Override
       public void run() {
           try {
               Thread.sleep(3000);
               mTestHandler.sendEmptyMessage(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }).start();
}

在對某件實物進一步瞭解以前,咱們要先對該事物的價值意義有一個理解,即他是作什麼的,再明白事物產生或發生時作了什麼,結束時又會有什麼樣的結果。性能優化

咱們要討論研究的是這個過程到底經歷了什麼,是發生什麼因,再經歷什麼產生這個果。架構

當調用Handler發送消息相關方法時,會把這個消息發送到哪兒去?從上面的示例代碼中能夠看到消息最終仍是會回到Handler手上,由他本身處理。咱們要搞清楚的就是這個消息由發到收的過程。異步

消息會發送到哪兒去?

mTestHandler.sendEmptyMessage(1);async

咱們追隨sendEmptyMessage()方法下去:ide

Handler不管以何種方式發送何種消息,都會通過到sendMessageAtTime()方法:

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的mQueue對象是否爲空,再調用enqueueMessage()方法,從字面意思不難理解是將該消息入隊保存起來。再看enqueueMessage()方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   msg.target = this;
   if (mAsynchronous) {
       msg.setAsynchronous(true);
   }
   return queue.enqueueMessage(msg, uptimeMillis);
}

public final class Message implements Parcelable {
   //....
   Handler target;
}

該方法會先將Message和當前Handler綁定起來,不難理解當須要處理Message時直接甩給綁定他的Handler就是了。再調用queue.enqueueMessage()方法正式入隊,而queue對象究竟是一個什麼樣的對象?由單向鏈表實現的消息隊列。queue.enqueueMessage()方法就是遍歷鏈表將消息插入表尾保存起來,而從queue取消息就是把表頭的Message拿出來。

接着來搞清楚queue他是什麼時候怎樣建立的?來看Handler的構造函數。

public Handler(Callback callback, boolean async) {
   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的構造方法會先調用Looper.myLooper()方法看能不能獲取一個Looper對象,若是獲取不到程序就直接蹦了。再從該Looper對象中獲取咱們須要的消息隊列。

Looper究竟是一個怎樣的對象,有這怎樣的身份,在Handler機制中扮演這怎樣的角色?來看myLooper()方法:

public static @Nullable Looper myLooper() {
   return sThreadLocal.get();
}

myLooper()方法會直接就從sThreadLocal對象中獲取Looper,而sThreadLocal是一個ThreadLocal類對象,而ThreadLocal類說白了就是經過他存儲的對象是線程私有的。

static final ThreadLocal sThreadLocal = new ThreadLocal();

調用get()方法直接從ThreadLocal中獲取Looper,接下來就得看是什麼時候set()將Loooper對象保存到ThreadLocal中去的。Looper.prepare()方法:

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));
}

private Looper(boolean quitAllowed) {
   mQueue = new MessageQueue(quitAllowed);
   mThread = Thread.currentThread();
}

從這段源碼能夠看出,Looper不只是線程私有的仍是惟一不可替換。Looper對象建立時會初始化MessageQueue()對象,正是咱們須要的隊列。
之因此最上面的示例代碼中咱們並無調用prepare()方法初始化Looper,程序也沒有崩潰,那是由於在ActivityThread的Main方法中就已經初始化了Looper對象。

public final class ActivityThread {
   //......
   public static void main(String[] args) {
       Looper.prepareMainLooper();
   }
   //......
}

到此咱們算是明白消息會發送到哪兒去了,如今就要知道的是怎麼取出消息交給Handler處理的。

首先MessageQueue封裝有完整的添加(入隊)和獲取/刪除(出隊)方法,MessageQueeue.next()方法將鏈表當中表頭第一個消息取出。

Message next() {
   //..........
   for (;;) {
       if (nextPollTimeoutMillis != 0) {
           Binder.flushPendingCommands();
       }

       nativePollOnce(ptr, nextPollTimeoutMillis);

       synchronized (this) {
           final long now = SystemClock.uptimeMillis();
           Message prevMsg = null;
           Message msg = mMessages;
           if (msg != null && msg.target == null) {
               do {
                   prevMsg = msg;
                   msg = msg.next;
               } while (msg != null && !msg.isAsynchronous());
           }
           if (msg != null) {
               if (now < msg.when) {
                   nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
               } else {
                   mBlocked = false;
                   if (prevMsg != null) {
                       prevMsg.next = msg.next;
                   } else {
                       mMessages = msg.next;
                   }
                   msg.next = null;
                   if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                   msg.markInUse();
                   return msg;
               }
           } else {
               nextPollTimeoutMillis = -1;
           }

           if (mQuitting) {
               dispose();
               return null;
           }
           //............
       }
       //..............
   }
}

代碼雖然比較多,咱們從第三行和第39行開始提及。next()方法實際是一個死循環,會一直從當前隊列中去取Message,即便當前隊列沒有消息可取,也不會跳出循環,會一直執行,直到可以從隊列中取到消息next()方法纔會執行結束。

其次當Looper調用quit()方法,mQuitting變量爲ture時會跳出死循環,next()方法返回null方法也會執行結束。

上面提到在ActivityThread中的main()方法中會初始化Looper,其實在不久以後便會開始從隊列中取消息。

public static void main(String[] args) {

   //......
   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"));
   }

   Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
   Looper.loop();

   throw new RuntimeException("Main thread loop unexpectedly exited");
}

調用Looper.loop()方法就會開始遍歷取消息。

public static void loop() {
for (;;) {
   Message msg = queue.next(); // might block
   if (msg == null) {
       return;
   }

   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);
       }
   }
}

loop()方法中也是一個死循環,調用queue.nex()方法開始阻塞式取消息,只有手動讓Looper中止,next()方法纔會返回null。

取到消息後,調用dispatchMessage()方法把消息交由Handler處理。

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);
   }
}

不只能夠給Handler設置回調接口,Message也行。默認狀況下會回調handleMessage()方法。

本覺得說得差很少了,其實還有一個關鍵的問題。咱們是在主線程中執行的loop()方法,死循環爲何沒有形成Activity阻塞卡死?查閱資料Android中爲何主線程不會由於Looper.loop()裏的死循環卡死後得知next()方法中會執行一個重要方法。

nativePollOnce(ptr, nextPollTimeoutMillis);

大佬分析得很好,我就很少說了。提一點,咱們發送的延時消息,會經過Message字段/變量when,將時長保存下來,延時也是經過這個方法作到的。

Message next() {

   final long now = SystemClock.uptimeMillis();

   if (msg != null) {
       if (now < msg.when) {
           // Next message is not ready.  Set a timeout to wake up when it is ready.
           nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
       } else {
           //......
       }
   }
}

總結,Handler發送消息會將消息保存到Looper維護的消息隊列MessageQueue中去,而Looper會死循環一直從隊列中取消息,取到消息後會交由Message綁定的Handler回調處理。

若是文字不夠,那就拿出個人終極學習祕籍,來自三星架構師講解的Handler通訊機制源碼講解,裏面還結合了BAT的面試題,視頻我已經上傳到b站:https://www.bilibili.com/video/av78745845

第一次看文章的朋友能夠關注我,會不按期發佈大廠面試題、Android架構技術知識點及解析等內容,還有學習PDF+源碼筆記+面試文檔+進階視頻分享

相關文章
相關標籤/搜索