編輯:關於Android編程
初次看到HandlerThread的名字,我們可能會聯想到Handler和Thread這兩個類,沒錯,它其實就是跟Handler和Thread有莫大的關系。HandlerThread繼承自Thread,它本質上就是一個Thread,而且專門用來處理Handler的消息。
看看官方對它的解釋:
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可以創建一個帶有looper的線程,looper對象可以用於創建Handler類來進行來進行調度,而且start()方法必須被調用。
在Android開發中,不熟悉多線程開發的人一想到要使用線程,可能就用new Thread(){…}.start()這樣的方式。實質上在只有單個耗時任務時用這種方式是可以的,但若是有多個耗時任務要串行執行呢?那不得要多次創建多次銷毀線程,這樣導致的代價是很耗系統資源,容易存在性能問題。那麼,怎麼解決呢?
我們可以只創建一個工作線程,然後在裡面循環處理耗時任務,創建過程如下:
Handler mHandler; private void createWorkerThread() { new Thread() { @Override public void run() { super.run(); Looper.prepare(); mHandler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { ...... } }; Looper.loop(); } }.start(); }
在該工作線程中,
- 調用Looper.prepare()創建與當前線程綁定的Looper實例;
- 使用上面創建的Looper生成Handler實例;
- 調用Looper.loop()實現消息循環;
然後透過Looper的循環,在Handler的handlerMessage()中進行異步任務的循環處理。而這也正好是HandlerThread的實現。
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
那麼我們看下HandlerThread有哪些特點:
- HandlerThread本質上是一個線程類,它繼承了Thread;
- HandlerThread有自己的內部Looper對象,通過Looper.loop()進行looper循環;
- 通過獲取HandlerThread的looper對象傳遞給Handler對象,然後在handleMessage()方法中執行異步任務;
- 創建HandlerThread後必須調用HandlerThread.start()方法來啟動線程。
HandlerThread handlerThread = new HandlerThread("Handler Thread"); //HandlerThread handlerThread = new HandlerThread("Handler Thread",Process.THREAD_PRIORITY_DEFAULT);
HandlerThread默認有兩個構造函數,提供了線程名參數和線程優先級參數的設置。
handlerThread.start();
通過start()方法就可以啟動一個HandlerThread了,該線程會不斷地循環運行。
Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //運行在工作線程(子線程)中,用於實現自己的消息處理 return true; } });
通過將HandlerThread綁定的Looper對象傳遞給Handler作為參數,構建一個異步的Handler對象,為了能實現耗時任務的異步執行,我們重寫了Handler的Callback接口的handleMessage()方法,當然也可以不重寫該方法,而通過post()方法進行耗時任務操作。
Handler workderHandler = new Handler(handlerThread.getLooper()); workderHandler.post(new Runnable() { @Override public void run() { //運行在工作線程(子線程)中,用於實現自己的消息處理 } });
最後,我們就可以通過調用workerHandler以發送消息的形式發送耗時任務到工作線程HandlerThread中去執行,實際上就是在Handler.Callback裡的handleMessage()中執行。
這裡要注意,在創建Handler作為HandlerThread線程消息執行者的時候必須先調用start()方法,因為創建Handler所需要的Looper參數是從HandlerThread中獲得的,而Looper對象的賦值又是在HandlerThread的run()方法中創建。
import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by Administrator on 2016/9/18. */ public class HandlerThreadActivity extends Activity implements Handler.Callback { private DBHandlerThread mDBHandlerThread; private Handler mUIHandler; //與UI線程相關聯的Handler Button mBtnQuery; TextView mTextResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnQuery = (Button) findViewById(R.id.buttonQuery); mTextResult = (TextView) findViewById(R.id.result); mBtnQuery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //將異步耗時任務發送到HandlerThread中 //Message msg = Message.obtain(null, DBHandlerThread.MSG_QUERY_FRIENDS); //mDBHandlerThread.getWorkerHandler().sendMessage(msg); mDBHandlerThread.queryFriends(); } }); mUIHandler = new Handler(this); initWorkerThread(); } protected void initWorkerThread() { mDBHandlerThread = new DBHandlerThread("Handler Thread"); mDBHandlerThread.setUIHandlerCallBack(mUIHandler); mDBHandlerThread.start(); //start()後會執行Thread的run()方法 } @Override protected void onDestroy() { super.onDestroy(); mDBHandlerThread.setUIHandlerCallBack(null); mDBHandlerThread.quit(); mDBHandlerThread = null; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case DBHandlerThread.MSG_QUERY_FRIENDS: // update UI break; } return false; } }
import android.os.Handler; import android.os.HandlerThread; import android.os.Message; /** * Created by Administrator on 2016/9/18. */ public class DBHandlerThread extends HandlerThread implements Handler.Callback { public static final int MSG_QUERY_FRIENDS = 100; private Handler mWorkerHandler; //與工作線程相關聯的Handler private Handler mUIHandler; //與UI線程相關聯的Handler public DBHandlerThread(String name) { super(name); } public DBHandlerThread(String name, int priority) { super(name, priority); } public void setUIHandlerCallBack(Handler handler) { this.mUIHandler = handler; } public Handler getWorkerHandler() { return mWorkerHandler; } @Override protected void onLooperPrepared() { mWorkerHandler = new Handler(getLooper(), this); } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_QUERY_FRIENDS: //...查詢數據庫操作... Message message = Message.obtain(null, MSG_QUERY_FRIENDS); mUIHandler.sendMessage(message); //通知UI更新 break; } return true; } public void queryFriends() { Message msg = Message.obtain(null, MSG_QUERY_FRIENDS); mWorkerHandler.sendMessage(msg); } }
HandlerThread的源碼不多,先看下它的構造函數:
/** * 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. */ public class HandlerThread extends Thread { int mPriority; //線程優先級 int mTid = -1; //當前線程id //當前線程持有的Looper對象 Looper mLooper; public HandlerThread(String name) { //調用父類默認的方法創建線程 super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } //帶優先級參數的構造方法 public HandlerThread(String name, int priority) { super(name); mPriority = priority; } ............... }
從代碼中得知,HandlerThread帶有兩個構造函數,可傳遞兩個參數,一個參數是name,指的是線程的名稱,另一個參數是priority,指的是線程優先級。線程的優先級的取值范圍為-20到19。優先級高的獲得的CPU資源更多,反之則越少。-20代表優先級最高,19最低。我們可以根據自己的需要去設置線程的優先級,也可以采用默認的優先級,HandlerThread的默認優先級是Process.THREAD_PRIORITY_DEFAULT,具體值為0。該優先級是再run()方法中設置的,我們看它的run()方法:
public class HandlerThread extends Thread { /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); //獲得當前線程的id Looper.prepare(); //准備循環條件 //通過鎖機制來獲得當前線程的Looper對象 synchronized (this) { mLooper = Looper.myLooper(); //喚醒等待線程 notifyAll(); } //設置當前線程的優先級 Process.setThreadPriority(mPriority); //在線程循環之前做一些准備工作(子類可實現也可不實現) onLooperPrepared(); //啟動loop Looper.loop(); mTid = -1; } }
run()方法主要是通過Looper.prepare()和Looper.loop()構造了一個循環線程。這裡要注意,在創建HandlerThread對象後必須調用其start()方法才能進行run()方法體的執行。
在Looper.prepare()執行後,Looper對象會被創建,然後通過同步鎖機制,將Looper對象賦值給HandlerThread的內部變量mLooper,並通過notifyAll()方法去喚醒等待線程。接著為線程賦予優先級,然後執行onLooperPrepared()方法,該方法是一個空實現,留給我們必要的時候去重寫的,主要用來做一些初始化工作。最後通過執行Looper.loop()在線程中啟動消息隊列。
我們看到在run()方法中進行了喚醒等待線程,為什麼要這麼做呢?答案就在getLooper()方法中:
/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */ 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(); //等待,直到另一個線程調用notify()或notifyAll()來喚醒它 } catch (InterruptedException e) { } } } return mLooper; }
該方法用來獲取當前子線程HandlerThread所關聯的Looper對象實例。首先判斷HandlerThread線程是否存活,如果沒有存活就直接返回null,否則繼續執行,進入同步塊並判斷Looper對象是否為空以及線程是否啟動,若都滿足,則調用wait()方法進入阻塞階段,直到Looper對象被成功創建並且通過notifyAll()方法喚醒該等待線程,最後才返回該Looper對象。
Looper對象的創建是在run()方法進行的,也即在子線程中執行的,而getLooper()方法是在UI線程中調用的,若不使用等待喚醒機制,我們就無法保證在UI線程中調用getLooper()方法時Looper對象已經被創建,會面臨一個同步的問題,所以HandlerThread就通過等待喚醒機制來解決該同步問題。
摘要:Android L平台在圖形渲染方面有一項重要的改進,它引入了一個專門的線程用於執行渲染工作,UI線程負責生成的顯示列表(DisplayList),渲染線程負責重放
進度條(ProgressBar)java.lang.Object;android.view.View;android.widget.ProgressBar;Progres
項目中經常碰到需要處理大圖片的問題,因為android對應用分配資源的限制,如果不進行相應的處理,容易造成OOM。 Android處理大圖的方法: 對於大圖先獲取出圖片的
一、問題描述 為提高圖片加載的效率,需要對圖片的采用緩存和異步加載策略,編碼相對比較復雜,實際上有一些優秀的框架提供了解決方案,比如近期在git上比較活躍的xut