參考教程:http://www.sohu.com/a/237792762_659256
首先說一下這里面涉及到的線程:
1.mainLooper:
這個線程可以理解為消費者線程,里面運行了一個死循環,不斷進行消息出隊和處理消息這兩個動作。
2.workLooper:
這個線程就相當於是生產者線程,進行消息入隊。
3.程序本身的線程:
這個線程是隱式的,也就是我們運行程序的這個線程,知道有這樣一個東西的存在能幫助我們理解整個程序的運行。
然后介紹每一個類:
1.Message:
這個類的作用是存儲一個生產者生產出來的具體的消息,就類似鏈表隊列中的一個節點,自行定義需要存儲的內容。
code:消息要執行的具體動作代碼
msg:消息內容
target:用來關聯hadler,根本目的時為了使這幾個類共享一個MessageQueue,這個很重要
2.MessageQueue:
這個類就是生產者和消費者線程需要共享的一個存儲消息的隊列,生產者將消息放入這個隊列,消費者取出消息並處理。
內部實現是用了BlockingQueue,這個隊列特別的地方就是出隊和入隊的時候是阻塞的,也就是說當隊列中沒有元素的時候,出隊這個動作會引起線程阻塞,直到有元素入隊;同理入隊也會因為隊列滿而引起線程阻塞,直到有元素出隊。
這個類中定義了兩個方法:next和enqueueMessage分別對應出對和入隊。
需要考慮的就是如何將生產者和消費者多線程共享這個隊列?這個在下面解釋。
3.Looper
這個類是整個機制的核心,理解了這個類這個機制怎樣運行的也就基本清楚了。
這個類主要是為mainThread服務的,類中定義了一個靜態常量ThreadLocal,用來保存某個線程的共享變量,在這里它存的是一個:Looper,也就是這個類本身的一個實例。
mainThread這個線程需要在開始線程的時候通過Looper.prepareMainLooper()創建一個looper。然后調用loop(),這個函數就是mainThread的主循環,不斷地做兩件事:消息出隊和處理消息。
4.Handler:
這個類主要定義了兩個方法:sendMessage()和handleMessage(),即發送消息和處理消息,其中sendMessage()就是將消息入隊,而handleMessage()設計成抽象方法,根據不同的實際情況設計不同的消息處理方法。
介紹完這四個類之后,要思考的就是生產者和消費者對MessageQueue的共享問題:
主程序開啟了mainThread進程,這個進程在一開始就創建了一個Looper和一個mq,由於一開始這個mq是一個空隊列,mainThread執行到loop()里的for循環時被阻塞在msg = me.mq.next();此時的程序並不會因此停止,而是向下執行,創建了一個handler,hadler中傳入了mainThread中創建的那個Looper,並將這個Looper中的mq和hadler中的mq相關聯,換句話說,此時handler中的mq就是mainThread中looper的mq。程序接着往下走,又創建了一個workThread,這個線程傳入message,這個message除了保存了自身的信息之外,還保存了剛剛創建的handler,把這樣的message傳入workThread執行入隊操作時,就能夠將這個message存入handler中的mq中,此時也就將生產者和消費者這兩個線程的MessageQueue指向了同一片內存。
為什么要把MainThread創建的looper保存在ThreadLocal中?為什么要設置成static final?
這個東西叫做線程本地變量,也有些地方叫做線程本地存儲,其實意思差不多。ThreadLocal為變量在每個線程中都創建了一個副本,那么每個線程可以訪問自己內部的副本變量。
looper是在MainThread中創建的,也就是說每創建一個MainThead,這個MainThread中有就一個looper這樣一個變量,不同的MainThread中有不同的looper。
設置成static final 這樣就保證Looper這個類在創建的時,ThreadLocal就已經聲明和創建。
思考的還是不太全面,先把代碼先貼在這里:
Message.java
package Handler_test; public class Message { private int code; private String msg; Handler target; public Message() { } public Message(int code, String msg, Handler handler) { this.code = code; this.msg = msg; this.target = handler; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return this.msg; } public void setMsg(String msg) { this.msg = msg; } }
MessageQueue.java
package Handler_test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class MessageQueue implements IMessageQueue{ private BlockingQueue<Message> queue; public MessageQueue(int cap) { this.queue = new LinkedBlockingQueue<>(cap); } @Override public Message next() throws InterruptedException { // TODO Auto-generated method stub return queue.take(); } @Override public void enqueueMessage(Message msg) throws InterruptedException { // TODO Auto-generated method stub try { queue.put(msg); }catch(InterruptedException e) { e.printStackTrace(); } } }
Handler.java
package Handler_test; public abstract class Handler { private MessageQueue mq; public Handler(Looper looper) { mq = looper.mq; } public Handler() { Looper.myLooper(); } public void sendMessage(Message msg) { try { mq.enqueueMessage(msg); }catch(InterruptedException e){ e.printStackTrace(); } } public abstract void handleMessage(Message msg); }
Looper.java
package Handler_test; public class Looper { MessageQueue mq; //用來保存某個線程的共享變量 //為什么要做成常量? static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>(); private static Looper sMainLooper; public Looper() { mq = new MessageQueue(2); } public static void prepare() { if(sThreadLocal.get() != null) { throw new RuntimeException("一個線程只能創建一個looper"); } sThreadLocal.set(new Looper()); } public static void prepareMainLooper() { prepare(); //??直接寫Looper行么 synchronized(Looper.class) { if(sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } public static Looper myLooper() { // TODO Auto-generated method stub return sThreadLocal.get(); } public static Looper getMainLooper() { return sMainLooper; } public static void Loop() { final Looper me = myLooper(); if(me == null) { throw new RuntimeException("No looper; Looper.prepared() wasn't called on this thread"); } for(;;) { Message msg = null; try { msg = me.mq.next(); }catch(InterruptedException e) { e.printStackTrace(); } if(msg != null) { msg.target.handleMessage(msg); } } } }
Main.java
package Handler_test; import java.util.Random; public class Main { public static void main(String[] args) { MainThread mainThread = new MainThread(); //這里start()創建一個執行run()方法的新線程 //調用start方法表示此進程處於 可運行狀態 ,但並不一定正在運行 //線程的調度依賴系統提供的服務 mainThread.start(); //mainThread在准備looper時耗時,而程序的讀取不會因為線程的耗時而停止 //但是之后的程序需要mainThread創建好looper后才能執行 //我認為Thread.sleep(100);是表示這個程序的線程, //而不是mainThread以及workThread中的任何一個 //當looper創建好后Looper.getMainLooper()判斷非空,跳出循環,程序向下執行 //mainThread在創建完looper后由於mq中沒有消息而卡在了msg = me.mq.next(); while(Looper.getMainLooper() == null) { try { Thread.sleep(100); }catch(InterruptedException e) { e.printStackTrace(); } } //這里傳入了mainThread中創建的looper //handler的構造函數中:mq = looper.mq;也將looper中的mq傳給了handler //這樣looper中的mq就和handler中的mq關聯起來了 //也就是共用一片內存 Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { System.out.println("execute in: " + Thread.currentThread().getName()); switch(msg.getCode()) { case 0: System.out.println("0 case"); break; case 1: System.out.println("1 case"); break; case 2: System.out.println("2 case"); break; default: System.out.println("default"); break; } } }; //將前面寫好的handler傳入msg中 Message msg1 = new Message(0, "first", handler); WorkThread workThread1 = new WorkThread(handler, msg1); Message msg2 = new Message(2, "two", handler); WorkThread workThread2 = new WorkThread(handler, msg2); workThread1.start(); workThread2.start(); //workThread中的run方法執行入隊操作handler.sendMessage(message); //這時將msg放入了handler中定義的那個mq //這個mq同時也是looper中的那個mq //此時由於mq這個隊列不為空,mainThread被喚醒 //繼續執行msg.target.handleMessage(msg); //由於loop()是一個死循環,mainThread在處理完一條msg之后會繼續取下一條msg //循環這個過程 } //為什么要把這兩個線程做成內部類,放在外面不行么? public static class WorkThread extends Thread{ private Handler handler; private Message message; public WorkThread(Handler handler, Message message) { setName("Work Thread"); this.handler = handler; this.message = message; } @Override public void run() { //這句話可以不寫么? super.run(); //模擬耗時 Random random = new Random(); try { Thread.sleep(random.nextInt(10)*30); }catch(InterruptedException e) { e.printStackTrace(); } //消息入隊 handler.sendMessage(message); } } public static class MainThread extends Thread{ public MainThread() { setName("MainThread"); } @Override public void run() { super.run(); Looper.prepareMainLooper(); System.out.println(getName() + " the looper is prepared."); Looper.Loop(); } } }