亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Handler相關面試題有哪些

發布時間:2021-11-18 15:29:07 來源:億速云 閱讀:170 作者:iii 欄目:移動開發

本篇內容主要講解“Handler相關面試題有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Handler相關面試題有哪些”吧!

1.獲取Message實例的方式有哪些?哪一種更好?

獲取Message實例的方法主要有兩種,一種是直接創建,Message msg = new Message。另一種是通過Message.obtain()或者Handler.obtatinMessage()來得到一個Message對象。更推薦使用后一種方式,這種方式得到的對象是從對象回收池中得到,復用已經處理完的Message對象,而不是重新生成一個新對象。Message.obtain()Handler.obtatinMessage()最終都是調用了Message類的obtain()方法,查看方法內容,就知道是從對象回收池里得到Message。

  public static Message obtain() {
          synchronized (sPoolSync) {
              if (sPool != null) {
                  Message m = sPool;
                  sPool = m.next;
                  m.next = null;
                  m.flags = 0; // clear in-use flag
                  sPoolSize--;
                  return m;
              }
          }
          return new Message();
    }

2.當Activity有多個Handler的時候,Message消息是否會混亂?怎么樣區分當前消息由哪個Handler處理?

不會混亂,哪個Handler發送的消息,到時候也是這個handler處理。在發送消息的時候,會綁定target,這個target就是Handler本身,當需要handler調用dispatchMessage(msg)處理消息的時候,這個Handler就是發送消息時綁定的handler。
無論用哪一種方法發送消息,最終都會調用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)來發送消息

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

這里的this,就是當前的handler。在來看需要Handler處理消息的時候,取的是哪一個handler,下面貼出主要源碼。

 public static void loop() {
  ......
        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
         ......
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           ......
            msg.recycleUnchecked();
        }
    }

這是循環消息時的部分代碼,處理消息代碼是msg.target.dispatchMessage(msg);,這里的target就是當時發送消息的handler。

3.在子線程發送消息,卻能夠在主線程接收消息,主線程和子線程是怎么樣切換的?

子線程用handler發送消息,發送的消息被送到與主線程相關聯的MessageQueue,也是主線程相關聯的Looper在循環消息,handler所關聯的是主線程的Looper和MessageQueue,所以最后消息的處理邏輯也是在主線程。只有發送消息是在子線程,其它都是在主線程,Handler與哪個線程的Looper相關聯,消息處理邏輯就在與之相關的線程中執行,相應的消息的走向也就在相關聯的MessageQueue中。所以子線程切換到主線程是很自然的過程,并沒有想象中的復雜。

4.能不能在子線程中創建Handler?

可以,但是在創建前先調用prepare()方法創建Looper。Handler創建的時候,會去檢查是否有創建Looper,如果沒有創建就會拋出異常。相關源碼如下:

 public Handler(Callback callback, boolean async) {
     ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

所以我們在子線程需要先調用prepare()方法創建Looper。這里還多提一點,在主線程創建就不需要自己創建Looper,因為在ActivityTread類里面,已經為我們創建好了,相關源碼如下:

 public static void main(String[] args) {
     ......
        Looper.prepareMainLooper();// 為主線程創建looper
        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        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。 所以在主線程中直接創建一個Handler,就直接可以循環消息,因為安卓的主線程已經為我們準備好了Looper。

5.一個線程可以有幾個Handler?幾個Looper?

一個線程可以有多個Handler,但是只有一個Looper。創建Handler之前,需要創建Looper,否則會報錯。源碼里面已經做了說明。

 public Handler(Callback callback, boolean async) {
   ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {//判斷Looper是否被創建
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在來看Looper的創建,是在prepare()方法里。

  // sThreadLocal.get() will return null unless you've called prepare().
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  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是否存在,存在就會拋出Only one Looper may be created per thread異常,這是在告訴我們一個線程只能有一個Looper。而TreadLocal的作用就是線程間隔離,確保一個線程對應一個Looper。還可以看看Looperg構造方法的源碼

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

MessageQueue的創始化是在Looper的構造方法里。不管一個線程有多少個Handler,相關聯的都是同一個Looper和MessageQueue。

關于Handler,可以問的問題有很多,以上只是抽出一些我認為比較重要的問題。在尋找答案以后,我將Handler機制的整個過程在腦海中過了一遍,并且畫了個草圖。

Handler機制中重要類的相互關聯圖

Handler相關面試題有哪些

Handler機制原理涉及幾個重要的類:Handler、Message、MessageQueue、Looper。
就用子線程向主線程發送消息來說明整個過程。
首先在主線程創建一個Handler,在Handler類里面會創建Looper以及MessageQueue的對象,并且在Handler構造方法里面賦值

 final Looper mLooper;
 final MessageQueue mQueue;
 public Handler(Callback callback, boolean async) {
 ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

mLooper = Looper.myLooper();得到的Looper是主線程的Looper
mQueue = mLooper.mQueue;得到的MessageQueue就是在Looper構造方法里面創建的MessageQueue。
創建好了Handler實例,我們就會在子線程調用handler.sendMessage(msg);發送消息,將message放到MessageQueue里面。在enqueueMessage()里面就給每個message設置target,這個target就是當前的handler。

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

再然后調用Looper.loop();(在ActivityThread的main()里面已經幫我們寫好)開始循環消息,拿到消息以后就會用handler取出消息進行處理,重點代碼是msg.target.dispatchMessage(msg);,這里的handler就和一開始我們為message設置的Handler對應。

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

最后我們在Handler的handleMessage(msg)里面去處理我們的消息。
這就是子線程給主線程發送消息的整個過程,源碼很多,我只是截取了部分重點。這里多提一點就是線程處理消息。 在線程中處理消息需要做三件事情

1. 先創建一個Looper (Looper.prepare()) 2. 再創建Handler,默認是和當前線程的Looper關聯起來 3. 循環消息(Looper.loop()

這三個步驟的順序不能調換。因為主線程已經幫我們創建了Looper,所以我們不需要寫,如果是在子線程創建Looper就需要了。

Handler機制的理解要靠自己去琢磨,不斷的看源碼,去理解,理清它們之間的互相調用。只有比較深入的理解了Handler,才能在面試中回答面試官的問題,靠死記硬背是不可取的。

到此,相信大家對“Handler相關面試題有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武平县| 乌兰县| 稻城县| 汤原县| 双城市| 德惠市| 区。| 昌乐县| 电白县| 邻水| 晋城| 安福县| 巴彦淖尔市| 弥渡县| 舞阳县| 南涧| 平陆县| 皋兰县| 龙口市| 深水埗区| 额尔古纳市| 关岭| 凉城县| 永济市| 大城县| 申扎县| 安义县| 辽阳县| 景泰县| 绥阳县| 芜湖市| 巴青县| 惠州市| 乌鲁木齐市| 拉萨市| 固阳县| 镇巴县| 绍兴市| 潜江市| 肃北| 马龙县|