溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

面試必問(wèn)的一個(gè)知識(shí)點(diǎn),關(guān)于HandlerThread的使用場(chǎng)景以及怎樣使用 HandlerThrea

發(fā)布時(shí)間:2020-07-28 20:33:33 來(lái)源:網(wǎng)絡(luò) 閱讀:432 作者:Android丶VG 欄目:移動(dòng)開(kāi)發(fā)

前言

有些同學(xué)老問(wèn)我,快要校招了 今年的金三銀四退成了金四銀五了,我改準(zhǔn)備哪些知識(shí)點(diǎn)去面試復(fù)習(xí),然而要準(zhǔn)備的知識(shí)點(diǎn)很龐大,今天要講的知識(shí)其中之一。一篇關(guān)于HandlerThread的解析

文檔筆記~
面試必問(wèn)的一個(gè)知識(shí)點(diǎn),關(guān)于HandlerThread的使用場(chǎng)景以及怎樣使用 HandlerThrea

知識(shí)匯總的PDF相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

使用場(chǎng)景

HandlerThread是Google幫我們封裝好的,可以用來(lái)執(zhí)行多個(gè)耗時(shí)操作,而不需要 多次開(kāi)啟線程,里面是采用HandlerLooper實(shí)現(xiàn)的。

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called

怎樣使用HandlerThread
1. 創(chuàng)建HandlerThread的實(shí)例對(duì)象
  HandlerThread handlerThread = new HandlerThread("myHandlerThread" );

該參數(shù)表示線程的名字,可以隨便選擇。

1. 啟動(dòng)我們創(chuàng)建的HandlerThread線程
  handlerThread.start();

將我們的handlerThread與Handler綁定在一起。 還記得是怎樣將Handler與線 程對(duì)象綁定在一起的嗎?其實(shí)很簡(jiǎn)單,就是將線程的looperHandler綁定在一 起,代碼如下:

  mThreadHandler = new Handler(mHandlerThread.getLooper()) { 
   @Override 
   public void handleMessage(Message msg) { 
     checkForUpdate(); 
     if(isUpdate){ 
        mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); 
     } 
   }
  };

注意必須按照以上三個(gè)步驟來(lái),下面在講解源碼的時(shí)候會(huì)分析其原因

完整測(cè)試代碼如下
  public class MainActivity extends AppCompatActivity { 
     private static final int MSG_UPDATE_INFO = 0x100; 
     Handler mMainHandler = new Handler(); 
     private TextView mTv; 
     private Handler mThreadHandler; 
     private HandlerThread mHandlerThread; 
     private boolean isUpdate = true; 

     @Override 
     protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        mTv = (TextView) findViewById(R.id.tv); 
        initHandlerThread(); 
     }

     private void initHandlerThread() {
        mHandlerThread = new HandlerThread("xujun"); 
        mHandlerThread.start(); 
        mThreadHandler = new Handler(mHandlerThread.getLooper()) { 

          @Override 
          public void handleMessage(Message msg) { 
             checkForUpdate(); 
             if (isUpdate) { 
                 mThreadHandler.sendEmptyMessage(MSG_UPDATE_I NFO); 
             } 
          } 
      };
   }

   /**
    * 模擬從服務(wù)器解析數(shù)據(jù) 
    */ 
    private void checkForUpdate() { 
       try {
           //模擬耗時(shí) 
           Thread.sleep(1200); 
           mMainHandler.post(new Runnable() { 
                @Override 
                public void run() { 
                   String result = "實(shí)時(shí)更新中,當(dāng)前股票行情:<font color='red'>%d</font>"; 
                   result = String.format(result, (int) (Math.r andom() * 5000 + 1000)); 
                   mTv.setText(Html.fromHtml(result)); 
                } 
           }); 
    } catch (InterruptedException e) { 
           e.printStackTrace(); 
    } 
  }

   @Override 
   protected void onResume() { 
      isUpdate = true;
      super.onResume(); 
      mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO); 
    }

    @Override 
    protected void onPause() { 
       super.onPause(); 
       isUpdate = false; 
       mThreadHandler.removeMessages(MSG_UPDATE_INFO); 
    }

    @Override 
    protected void onDestroy() { 
       super.onDestroy(); 
       mHandlerThread.quit(); 
       mMainHandler.removeCallbacksAndMessages(null); 
    }
   }

運(yùn)行以上測(cè)試代碼,將可以看到如下效果圖(例子不太恰當(dāng),主要使用場(chǎng)景是在 handleMessage中執(zhí)行耗時(shí)操作)
面試必問(wèn)的一個(gè)知識(shí)點(diǎn),關(guān)于HandlerThread的使用場(chǎng)景以及怎樣使用 HandlerThrea

HandlerThread源碼分析

官方源代碼如下,是基于sdk23的,可以看到,只有一百多行代碼而已。

  public class HandlerThread extends Thread {
     int mPriority; 
     int mTid = -1; 
     Looper mLooper; 

     public HandlerThread(String name) { 
        super(name); 
        mPriority = Process.THREAD_PRIORITY_DEFAULT; 
     }

     public HandlerThread(String name, int priority) { 
        super(name); 
        mPriority = priority; 
     }

     /**
      * Call back method that can be explicitly overridden if nee ded to execute some 
      * setup before Looper loops. 
      */ 
      protected void onLooperPrepared() { 
      }

      @Override 
      public void run() { 
         mTid = Process.myTid(); 
         Looper.prepare(); 
         //持有鎖機(jī)制來(lái)獲得當(dāng)前線程的Looper對(duì)象 
         synchronized (this) { 
             mLooper = Looper.myLooper(); 
             //發(fā)出通知,當(dāng)前線程已經(jīng)創(chuàng)建mLooper對(duì)象成功,這里主要是通知g etLooper方法中的wait notifyAll(); 
         }
         //設(shè)置線程的優(yōu)先級(jí)別 
         Process.setThreadPriority(mPriority); 
         //這里默認(rèn)是空方法的實(shí)現(xiàn),我們可以重寫(xiě)這個(gè)方法來(lái)做一些線程開(kāi)始之前 的準(zhǔn)備,方便擴(kuò)展 
         onLooperPrepared(); 
         Looper.loop(); 
         mTid = -1;
     }
     public Looper getLooper() { 
        if (!isAlive()) { 
            return null;
        }
       // 直到線程創(chuàng)建完Looper之后才能獲得Looper對(duì)象,Looper未創(chuàng)建成功 ,阻塞 synchronized (this) { 
             while (isAlive() && mLooper == null) { 
                try {
                    wait(); 
                } catch (InterruptedException e) { 
                } 
             } 
       }
       return mLooper; 
     }

     public boolean quit() { 
       Looper looper = getLooper(); 
       if (looper != null) { 
           looper.quit(); 
           return true;
       }
       return false; 
     }

     public boolean quitSafely() { 
       Looper looper = getLooper(); 
       if (looper != null) { 
           looper.quitSafely(); 
           return true; 
       }
       return false; 
     }

     /**
      * Returns the identifier of this thread. See Process.myTid( ).
      */ 
      public int getThreadId() { 
        return mTid; 
      } 
  }

1)首先我們先來(lái)看一下它的構(gòu)造方法

  public HandlerThread(String name) { 
     super(name); 
     mPriority = Process.THREAD_PRIORITY_DEFAULT; 
  }

  public HandlerThread(String name, int priority) { 
     super(name); 
     mPriority = priority; 
  }

有兩個(gè)構(gòu)造方法,一個(gè)參數(shù)的和兩個(gè)參數(shù)的,name代表當(dāng)前線程的名稱(chēng),priority 為線程的優(yōu)先級(jí)別

2)接著我們來(lái)看一下run()方法,在run方法里面我們 可以看到我們會(huì)初始化一個(gè)Looper,并設(shè)置線程的優(yōu) 先級(jí)別

  public void run() { 
     mTid = Process.myTid(); 
     Looper.prepare(); 
     //持有鎖機(jī)制來(lái)獲得當(dāng)前線程的Looper對(duì)象 
     synchronized (this) { 
         mLooper = Looper.myLooper(); 
         //發(fā)出通知,當(dāng)前線程已經(jīng)創(chuàng)建mLooper對(duì)象成功,這里主要是通知getLo oper方法中的wait notifyAll(); 
     }
     //設(shè)置線程的優(yōu)先級(jí)別 
     Process.setThreadPriority(mPriority); 
     //這里默認(rèn)是空方法的實(shí)現(xiàn),我們可以重寫(xiě)這個(gè)方法來(lái)做一些線程開(kāi)始之前的準(zhǔn)備 ,方便擴(kuò)展 
     onLooperPrepared(); 
     Looper.loop(); 
     mTid = -1; 
  }
  • 還記得我們前面我們說(shuō)到使用HandlerThread的時(shí)候必須調(diào)用 start() 方 法,接著才可以將我們的HandlerThread和我們的handler綁定在一起嗎?其實(shí) 原因就是我們是在 run() 方法才開(kāi)始初始化我們的looper,而我們調(diào)用 HandlerThreadstart() 方法的時(shí)候,線程會(huì)交給虛擬機(jī)調(diào)度,由虛擬機(jī) 自動(dòng)調(diào)用run方法
    mHandlerThread.start(); 
    mThreadHandler = new Handler(mHandlerThread.getLooper()) { 
       @Override 
       public void handleMessage(Message msg) { 
          checkForUpdate(); 
          if(isUpdate){ 
             mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
          } 
       } 
    };
  • 這里我們?yōu)槭裁匆褂面i機(jī)制和 notifyAll() ;,原因我們可以 從 getLooper() 方法中知道
    public Looper getLooper() { 
     if (!isAlive()) { 
        return null; 
     }
     // 直到線程創(chuàng)建完Looper之后才能獲得Looper對(duì)象,Looper未創(chuàng)建成功,阻塞 
     synchronized (this) { 
        while (isAlive() && mLooper == null) { 
             try {
                 wait(); 
             } catch (InterruptedException e) { 
             } 
        } 
     }
     return mLooper; 
    }

    總結(jié): 在獲得mLooper對(duì)象的時(shí)候存在一個(gè)同步的問(wèn)題,只有當(dāng)線程創(chuàng)建成功并且 Looper對(duì)象也創(chuàng)建成功之后才能獲得mLooper的值。這里等待方法wait和run方法 中的notifyAll方法共同完成同步問(wèn)題。

3)接著我們來(lái)看一下quit方法和quitSafe方法

  //調(diào)用這個(gè)方法退出Looper消息循環(huán),及退出線程 
  public boolean quit() { 
     Looper looper = getLooper(); 
     if (looper != null) {
         looper.quit(); 
         return true; 
     }
     return false; 
  }
  //調(diào)用這個(gè)方法安全地退出線程 
  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) 
  public boolean quitSafely() { 
     Looper looper = getLooper(); 
     if (looper != null) { 
         looper.quitSafely(); 
         return true; 
     }
     return false;
  }

跟蹤這兩個(gè)方法容易知道只兩個(gè)方法最終都會(huì)調(diào)用MessageQueuequit(boolean safe) 方法

  void quit(boolean safe) { 
       if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); 
       }
       synchronized (this) { 
           if (mQuitting) { 
               return; 
           }
           mQuitting = true; 
           //安全退出調(diào)用這個(gè)方法 
           if (safe) { 
               removeAllFutureMessagesLocked(); 
           } else {//不安全退出調(diào)用這個(gè)方法 
               removeAllMessagesLocked(); 
           }
           // We can assume mPtr != 0 because mQuitting was previou sly false.nativeWake(mPtr); 
       } 
  }

不安全的會(huì)調(diào)用 removeAllMessagesLocked(); 這個(gè)方法,我們來(lái)看這個(gè)方法是 怎樣處理的,其實(shí)就是遍歷Message鏈表,移除所有信息的回調(diào),并重置為null。

  private void removeAllMessagesLocked() { 
     Message p = mMessages; 
     while (p != null) { 
           Message n = p.next; 
           p.recycleUnchecked(); 
           p = n; 
     }
     mMessages = null; 
  }

安全地會(huì)調(diào)用 removeAllFutureMessagesLocked(); 這個(gè)方法,它會(huì)根據(jù) Message.when這個(gè)屬性,判斷我們當(dāng)前消息隊(duì)列是否正在處理消息,沒(méi)有正在處 理消息的話,直接移除所有回調(diào),正在處理的話,等待該消息處理處理完畢再退出該循環(huán)。因此說(shuō) quitSafe() 是安全的,而 quit() 方法是不安全的,因?yàn)閝uit方 法不管是否正在處理消息,直接移除所有回調(diào)。

  private void removeAllFutureMessagesLocked() { 
     final long now = SystemClock.uptimeMillis(); 
     Message p = mMessages; 
     if (p != null) { 
        //判斷當(dāng)前隊(duì)列中的消息是否正在處理這個(gè)消息,沒(méi)有的話,直接移除所有 回調(diào) 
        if (p.when > now) { 
            removeAllMessagesLocked(); 
        } else {//正在處理的話,等待該消息處理處理完畢再退出該循環(huán) 
            Message n; 
            for (;;) { 
                n = p.next;
                if (n == null) { 
                    return; 
                }
                if (n.when > now) { 
                    break; 
                }
                p = n; 
           }
           p.next = null; 
           do {
               p = n; 
               n = p.next; 
               p.recycleUnchecked(); 
           } while (n != null); 
        } 
     } 
  }

文檔筆記~
面試必問(wèn)的一個(gè)知識(shí)點(diǎn),關(guān)于HandlerThread的使用場(chǎng)景以及怎樣使用 HandlerThrea

知識(shí)匯總的PDF相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI