淺析Handler、Looper機制。通過自訂Handler、Looper,讓你有更直觀的了觀!,handlerlooper

來源:互聯網
上載者:User

淺析Handler、Looper機制。通過自訂Handler、Looper,讓你有更直觀的了觀!,handlerlooper

轉載請註明出處:http://blog.csdn.net/liu470368500/article/details/40625333

Handler、Looper。面試的時候被問到的機率非常高的一個問題。當然。我說的是像我們這樣的初級。。。一般講解Handler、Looper機制的都是通過源碼去講解。這裡我來通過自訂Handler、Looper。讓各位看官能有個更直觀的瞭解。相信有了這篇博文的基礎。再看Handler、Looper的源碼。理解起來就更容易了。


為了與安卓原生的相接軌。這裡自訂的Handler-Looper使用的邏輯基本與系統原生的相一致。


先貼MyHanlder代碼:


/** * 自訂Handler *  * @author lzh *  */public class MyHandler {// 用於進行線程間通訊的阻塞隊列private BlockingQueue<MyMessage> mQueue;// 處理訊息的回調private CallBack callBack;public MyHandler(CallBack callBack) {super();MyLooper looper = MyLooper.myLooper();if (looper == null) {throw new RuntimeException("在新開的線程中。建立MyHandler對象需要先調用MyLooper.prepare()方法。");}mQueue = looper.mQueue;this.callBack = callBack;}/** * 訊息接收的回調 *  * @author Administrator *  */public interface CallBack {/** * 處理訊息 *  * @param msg */void handleMessage(MyMessage msg);}/** * 發送訊息 *  * @param msg */public void sendMessage(MyMessage msg) {msg.target = this;try {mQueue.put(msg);} catch (InterruptedException e) {}}/** * 派發訊息 *  * @param msg */public void dispatchMessage(MyMessage msg) {callBack.handleMessage(msg);}}

再看MyLooper的代碼:


public class MyLooper {private static ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<MyLooper>();private static MyLooper myLooper;/** 一個線程對應一個阻塞隊列。 */public BlockingQueue<MyMessage> mQueue = null;private MyLooper() {super();mQueue = new LinkedBlockingQueue<MyMessage>();}/** * 為本線程準備對應的MyLooper對象 */public static void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one MyLooper may be created per thread");}sThreadLocal.set(new MyLooper());}/** * 擷取當前線程相對應的Looper對象 *  * @return 當未調用prepare()方法時。ThreadLocal.get()方法返回的為null; */public static MyLooper myLooper() {return sThreadLocal.get();}/** * 這裡啟動訊息迴圈 */public static void loop() {while (true) {try {myLooper = myLooper();BlockingQueue<MyMessage> mQueue = myLooper.mQueue;// take()方法是個阻塞方法。線程運行到此會阻塞住。以準備接收發過來的訊息MyMessage msg = mQueue.take();msg.target.dispatchMessage(msg);} catch (InterruptedException e) {// 當線程關閉的時候會出現此異常。此時退出迴圈return;}}}}

本來安卓原生中使用的是MessageQueue。但是卻死活建立不出來。這裡只有使用BlockQueue代替了。


當中的ThreadLocal可能有部分朋友有點陌生。這是線程局部變數。它的set方法和get()方法比較有意思。是和線程相關的。你在哪個線程裡面set變數進去。你在哪個線程裡面get()出來的就是哪個。所以在MyLooper中得先調用prepare()方法。先將與此線程相關的MyLooper執行個體建立出來加入進去。這樣便能儲存一個線程只有一個Looper。相應的也只有一個阻塞隊列。


接下來看MyMessage代碼:


public class MyMessage {public int msg1;public int msg2;public int what;public Object obj;public MyHandler target;public Runnable runnable;}

原生的Message由於是final標記的。而Message裡面存的Handler對象又比較重要。得要依靠它來指定最終的訊息應該發送給哪個Handler來接收。所以。這個也自訂了。


下面開始來測試。由於安卓不允許在UI線程中有阻塞操作。所以這裡我們使用SurfaceView在子線程中畫圖來測試是否可進行線程間通訊。


/** * 測試自訂的Handler與Looper的測試工程,由於內部有阻塞隊列。而安卓的機制是不允許此類的阻塞行為在主線程中出現。 * 所以此處用SurfaceView在子線程中進行測試 *  * @author Administrator *  */public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new MySurfaceView(this));}class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,CallBack {private static final String TAG = "MySurfaceView";private SurfaceHolder mHolder;Thread mThread;MyHandlerCreateRunnable mRunnable;private Paint mPaint;private MyHandler handler = null;public MySurfaceView(Context context) {super(context);// 初始化holder:mHolder = getHolder();mHolder.addCallback(this);mRunnable = new MyHandlerCreateRunnable();mPaint = new Paint();mPaint.setColor(Color.BLACK);mPaint.setAntiAlias(true);mPaint.setTextAlign(Align.CENTER);mPaint.setTextSize(45);new Thread(mRunnable).start();}public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public MySurfaceView(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.d(TAG, "==surfaceCreated==");// mQueue = new LinkedBlockingQueue<String>();new Thread(new MyTimerRunnable()).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Log.d(TAG, "==surfaceChanged==");}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.d(TAG, "==surfaceDestroyed==");}@Overridepublic void handleMessage(MyMessage msg) {synchronized (mHolder) {Canvas mCanvas = null;System.out.println("==lockCanvas==");mCanvas = mHolder.lockCanvas();// 鎖定畫布。之後就可以在此畫布上畫圖了。String content = (String) msg.obj;mCanvas.drawColor(Color.WHITE);mCanvas.drawText(content, 100, 400, mPaint);mHolder.unlockCanvasAndPost(mCanvas);//}}/** * 定時發送訊息的線程 *  * @author Administrator *  */class MyTimerRunnable implements Runnable {int index = 0;@Overridepublic void run() {while (true) {MyMessage msg = new MyMessage();msg.obj = "這是第" + index + "個";index++;handler.sendMessage(msg);if (index >= 50) {break;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}/** * 建立MyHandler的線程 *  * @author Administrator *  */class MyHandlerCreateRunnable implements Runnable {@Overridepublic void run() {MyLooper.prepare();handler = new MyHandler(MySurfaceView.this);MyLooper.loop();}}}}

好了。代碼都貼完了。現在結合到一塊來看。在MyHandlerCreateRunnable中。我們對調用了MyLooper.prepare()方法對當前線程進行了線程局部變數儲存。再建立出MyHandler對象。最後讓訊息迴圈啟動。這時。在此線程中如果沒有訊息到來。就會在MyLooper的loop()方法中。被阻塞隊列的take()方法所阻塞。直到有訊息到來。


然後我們在MyTimerRunnable中。對訊息進行建立。並使用在MyHandlerCreateRunnable線程中建立的handler對象。對訊息進行發送。為了方便。下面貼出局部代碼繼續分析


/** * 發送訊息 *  * @param msg */public void sendMessage(MyMessage msg) {msg.target = this;try {mQueue.put(msg);} catch (InterruptedException e) {}}

在此handler的sendMessage方法處。將MyHandler自身作為msg對象的一個成員變數賦值。再將些訊息存放入此訊息佇列中。放入之後。MyLooper.loop()方法中的take()方法就會擷取到些msg對象並解除阻塞。繼續運行。


/** * 這裡啟動訊息迴圈 */public static void loop() {while (true) {try {myLooper = myLooper();BlockingQueue<MyMessage> mQueue = myLooper.mQueue;// take()方法是個阻塞方法。線程運行到此會阻塞住。以準備接收發過來的訊息MyMessage msg = mQueue.take();msg.target.dispatchMessage(msg);} catch (InterruptedException e) {// 當線程關閉的時候會出現此異常。此時退出迴圈return;}}}


運行之後可以看到。通過調用msg.target.dispatchMessage(msg)方法將此message發送給之前我們用來發送訊息的MyHandler對象。


/** * 派發訊息 *  * @param msg */public void dispatchMessage(MyMessage msg) {callBack.handleMessage(msg);}

接著立馬就將此訊息對象發送給了自己定義的回調方法中。也就是我們handler用來處理訊息的回調方法。handleMessage。

所以。搞了半天。真正用來對線程間進行通訊的其實就是一個阻塞隊列。。。相信這個結論夠簡潔明了。。。這是幾乎完全仿照安卓原生的handler-looper邏輯來寫的。所以。如果你理解了這篇部落格。相信更進一步的看Handler-Looper源碼會通暢不少。。。

下面提供demo下載。

點擊下載demo


Android Handler機制 怎使用?

Handler對象與其調用者在同一線程中,如果在Handler中設定了延時操作,則調用線程也會堵塞。每個Handler對象都會綁定一個Looper對象,每個Looper對象對應一個訊息佇列(MessageQueue)。如果在建立Handler時不指定與其綁定的Looper對象,系統預設會將當前線程的Looper綁定到該Handler上。
在主線程中,可以直接使用new Handler()建立Handler對象,其將自動與主線程的Looper對象綁定;在非主線程中直接這樣建立Handler則會報錯,因為Android系統預設情況下非主線程中沒有開啟Looper,而Handler對象必須綁定Looper對象。這種情況下,需先在該線程中手動開啟Looper(Looper.prepare()-->Looper.loop()),然後將其綁定到Handler對象上;或者通過Looper.getMainLooper(),獲得主線程的Looper,將其綁定到此Handler對象上。
Handler發送的訊息都會加入到Looper的MessageQueue中。一說Handler包含兩個隊列:線程隊列和訊息佇列;使用Handler.post()可以將線程對象加入到線程隊列中;使用Handler.sendMessage()可以將訊息對象加入到訊息佇列中。通過源碼分析證實,Handler只有一個訊息佇列,即MessageQueue。通過post()傳進去的線程對象將會被封裝成訊息對象後傳入MessageQueue。
使用post()將線程對象放到訊息佇列中後,當Looper輪詢到該線程執行時,實際上並不會單獨開啟一個新線程,而仍然在當前Looper綁定的線程中執行,Handler只是調用了該線程對象的run()而已。如,在子線程中定義了更新UI的指令,若直接開啟將該線程執行,則會報錯;而通過post()將其加入到主線程的Looper中並執行,就可以實現UI的更新。
使用sendMessage()將訊息對象加入到訊息佇列後,當Looper輪詢到該訊息時,就會調用Handler的handleMessage()來對其進行處理。再以更新UI為例,使用這種方法的話,就先將主線程的Looper綁定在Handler對象上,重載handleMessage()來處理UI更新,然後向其發送訊息就可以了。
 
handler機制的原理

andriod提供了 Handler 和 Looper 來滿足線程間的通訊。
  Handler 先進先出原則。
  Looper類用來管理特定線程內對象之間的訊息交換(Message Exchange)。

  1)Looper: 一個線程可以產生一個Looper對象,由它來管理此線程裡的Message Queue(訊息佇列)。

  2)Handler: 你可以構造Handler對象來與Looper溝通,以便push新訊息到Message Queue裡;或者接收Looper從Message Queue取出)所送來的訊息。

  3) Message Queue(訊息佇列):用來存放線程放入的訊息。

  4)線程:UI thread 通常就是main thread,而Android啟動程式時會替它建立一個Message Queue。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.