標籤:
AndroidHandlerThread完全解析
轉載請標明出處:
http://blog.csdn.net/lmj623565791/article/details/47079737;
本文出自:【張鴻洋的部落格】
1、概述
話說最近股市變動不變,也成了熱火朝天的話題。不知道大家有沒有考慮做個即時更新股市資料的app呢?假設我們要做一個股市資料即時更新的app,我們可以在網上找個第三方的股市資料介面,然後在我們的app中每隔1分鐘(合適的時間)去更新資料,然後更新我們的UI即可。
當然了,本文不是要教大家做這樣一個app,只是舉個情境。言歸正傳,回到我們的HandlerThread,大家一定聽說過Looper、Handler、Message三者的關係(如果不瞭解,可以查看Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係),在我們的UI線程默默的為我們服務。其實我們可以借鑒UI線程Looper的思想,搞個子線程,也通過Handler、Message通訊,可以適用於很多情境。
對了,我之前寫過一篇博文Android Handler 非同步訊息處理機制的妙用 建立強大的圖片載入類,這篇博文中就在子線程中建立了Looper,Handler,原理和HandlerThread一模一樣,可惜當時我並不知道這個類,不過大家也可以當做HandlerThread應用情境進行學習。
2、HandlerThread執行個體
下面看我們類比大盤走勢的代碼,其實非常簡單,就一個Activity
package com.zhy.blogcodes.intentservice;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.text.Html;import android.widget.TextView;import com.zhy.blogcodes.R;public class HandlerThreadActivity extends AppCompatActivity{ private TextView mTvServiceInfo; private HandlerThread mCheckMsgThread; private Handler mCheckMsgHandler; private boolean isUpdateInfo; private static final int MSG_UPDATE_INFO = 0x110; //與UI線程管理的handler private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread_handler); //建立後台線程 initBackThread(); mTvServiceInfo = (TextView) findViewById(R.id.id_textview); } @Override protected void onResume() { super.onResume(); //開始查詢 isUpdateInfo = true; mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO); } @Override protected void onPause() { super.onPause(); //停止查詢 isUpdateInfo = false; mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO); } private void initBackThread() { mCheckMsgThread = new HandlerThread("check-message-coming"); mCheckMsgThread.start(); mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) { @Override public void handleMessage(Message msg) { checkForUpdate(); if (isUpdateInfo) { mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000); } } }; } /** * 類比從伺服器解析資料 */ private void checkForUpdate() { try { //類比耗時 Thread.sleep(1000); mHandler.post(new Runnable() { @Override public void run() { String result = "即時更新中,當前大盤指數:<font color=‘red‘>%d</font>"; result = String.format(result, (int) (Math.random() * 3000 + 1000)); mTvServiceInfo.setText(Html.fromHtml(result)); } }); } catch (InterruptedException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); //釋放資源 mCheckMsgThread.quit(); }}
可以看到我們在onCreate中,去建立和啟動了HandlerThread,並且關聯了一個mCheckMsgHandler。然後我們分別在onResume和onPause中去開啟和暫停我們的查詢,最後在onDestory中去釋放資源。
這樣就實現了我們每隔5s去服務端查詢最新的資料,然後更新我們的UI,當然我們這裡通過Thread.sleep()類比耗時,返回了一個隨機數,大家可以很輕易的換成真正的資料介面。
布局文庫
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"> <TextView android:id="@+id/id_textview" android:text="正在載入大盤指數..." android:layout_width="wrap_content" android:layout_height="wrap_content"/></RelativeLayout>
運行
別問我為什麼要用紅色!!!
ok,當然了,我們的效果很單調,但是你完全可以去擴充,比如ListView顯示使用者關注的股票資料。或者是其他的需要一直檢測更新的資料。
看到這,你是否好奇呢,HandlerThread的內部是如何做的呢?其實非常的簡單,如果你看過Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係估計掃幾眼就會了。
HandlerThread 源碼分析
對於所有Looper,Handler相關細節統一參考上面提到的文章。
我們輕輕的掀開HandlerThread的源碼,還記得我們是通過
mCheckMsgThread = new HandlerThread("check-message-coming"); mCheckMsgThread.start();
建立和啟動的對象,那麼隨便掃一眼:
package android.os;public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
看到了什麼,其實我們就是初始化和啟動了一個線程;然後我們看run()方法,可以看到該方法中調用了Looper.prepare(),Loop.loop();
prepare()呢,中建立了一個Looper對象,並且把該對象放到了該線程範圍內的變數中(sThreadLocal),在Looper對象的構造過程中,初始化了一個MessageQueue,作為該Looper對象成員變數。
loop()就開啟了,不斷的迴圈從MessageQueue中取訊息處理了,當沒有訊息的時候會阻塞,有訊息的到來的時候會喚醒。如果你不明白我說的,參考上面推薦的文章。
接下來,我們建立了一個mCheckMsgHandler,是這麼建立的:
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())
對應源碼
public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
mCheckMsgThread.getLooper()返回的就是我們在run方法中建立的mLooper。
那麼Handler的構造呢,其實就是在Handler中持有一個指向該Looper.mQueue對象,當handler調用sendMessage方法時,其實就是往該mQueue中去插入一個message,然後Looper.loop()就會取出執行。
好了,到這我們就分析完了,其實就幾行代碼;不過有一點我想提一下:
如果你夠細心你會發現,run方法裡面當mLooper建立完成後有個notifyAll(),getLooper()中有個wait(),這是為什麼呢?因為的mLooper在一個線程中執行,而我們的handler是在UI線程初始化的,也就是說,我們必須等到mLooper建立完成,才能正確的返回getLooper();wait(),notify()就是為瞭解決這兩個線程的同步問題。
不過對於這樣的線程間的同步問題,我非常喜歡使用Semaphore。
還記得在Android Handler 非同步訊息處理機制的妙用 建立強大的圖片載入類一文中有類似的說明:
如果你比較細心,可能會發現裡面還有一些訊號量的操作的代碼,如果你不瞭解什麼是訊號量,可以參考:Java 並發專題 : Semaphore 實現 互斥 與 串連池 。 簡單說一下mSemaphore(訊號數為1)的作用,由於mPoolThreadHander實在子線程初始化的,所以我在初始化前調用了mSemaphore.acquire去請求一個訊號量,然後在初始化完成後釋放了此訊號量,我為什麼這麼做呢?因為在主線程可能會立即使用到mPoolThreadHander,但是mPoolThreadHander是在子線程初始化的,雖然速度很快,但是我也不能百分百的保證,主線程使用時已經初始化結束。
哈,當時也有很多人問,為什麼使用這個Semaphore,到這裡我想大家應該清楚了。話說假設我當時真的HanderThread這個類,可能之前的代碼能簡化不少呢~
對了,你可能會問與Timer相比有什麼優勢呢?
直接參考這篇文章吧:Handler vs Timer
ok~~
歡迎關注我的微博http://weibo.com/u/3165018720
群號:463081660,歡迎入群
公眾號:hongyangAndroid
(歡迎關注,第一時間推送博文資訊)
原文連結本文由豆約翰部落格備份專家遠程一鍵發布
AndroidHandlerThread完全解析(轉載)