相關連接配接: Android 消息處理機制之一: Handler 與 Message
Android 消息處理機制之二: Message 中 obtain()源代碼剖析
Android 消息處理機制之三: Handler 中 sendMessage() 源代碼剖析
上一講我們學習Handler和Message的一些使用方式,我們知道Handler它會發送消息和處理消息,并且關聯一個子線程,如何發送消息入隊和出隊處理消息等這些都是交給Looper去管理分發的,也就是它是負責整個消息隊列運轉的一個類,這一講我們就來學習一下Android中的Looper的操作。
一、Looper類介紹
這個類是用來在一個線程中運作一個消息循環(Message),預設情況下線程是沒有一個消息循環來關聯它們的,在這個線程中調用prepare()方法來啟動一個循環,然後調用loop()就可以處理消息至到循環停止。
下面就是一個典型的例子實作一個Looper線程,使用 prepare()方法 和 loop()來建立一個初始的Handler并且能夠與消息循環(Looper)進行溝通關聯
【注意】:預設情況下的android新誕生的一個線程是沒有開啟一個消息循環(Looper)的,但是主線程除外,主線程系統會自動為其建立Looper對象,開啟消息循環。
二、程式Demo
1. 布局檔案定義一個Button和TextView,這裡不貼出來,讀者可以閱讀附件源碼
2. MainActivity.java
3. 程式執行結果... public class MainActivity extends Activity { private Button btn; private TextView txt; private MyHandler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... mHandler = new MyHandler(); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // 啟動線程 new Thread(new MyThread()).start(); } }); } public class MyThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub Message msg = Message.obtain(); msg.obj = "AHuier"; mHandler.sendMessage(msg); } } public class MyHandler extends Handler { public MyHandler() { super(); // TODO Auto-generated constructor stub } // Handler中有個傳遞Looper對象的構造方法,這個構造方法比較少用 public MyHandler(Looper looper) { super(looper); // TODO Auto-generated constructor stub } @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); txt.setText("接受子線程發送的消息 --->" + msg.obj); } } ... }
4. 【說明】: 在上面的代碼中我們并沒有去手動生成Looper對象,主線程依然可以完成接受子線程消息并顯示的操作,在這裡我們需要明白為什麼我們之前的例子中雖然沒有建立一個Looper去管理消息,但是子線程中發送消息依然能夠被主線程接受到,原因是因為我們主線程中已經存在了預設的一個Looper對象。
這裡我們在做一個小測試,我們給其生成一個Looper對象,在onCreate()方法中添加代碼如下:
5. 在上面的一個Demo中,我們是實作了子線程發送消息給主線程來更新UI的操作和Looper的關系,子線程預設情況下是沒有Looper的對象的,下面我就來測試一下主線程向子線程發送消息,由于子線程預設沒有Looper,我們就來測試一下這樣實作會發生什麼情況?[注意,這種方式我們一般在實際開發中是很少見的],Demo2如下所示:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initComponent(); // 在Activity中有一個預設的Looper對象,來處理子線程發送的消息 // 這裡我們嘗試的給其生成一個Looper對象,也是可以的 Looper looper = Looper.myLooper(); //獲得與子線程關聯的Looper對象 mHandler = new MyHandler(looper); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // 啟動線程 new Thread(new MyThread()).start(); } }); }
程式執行依然會接受到子線程發送的消息。為什麼會是這樣的呢?我們來檢視一下它們的源碼
1) 檢視Handler源碼中的構造方法
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }
可以發現在其構造方法中就已經預設為幫其生成一個Looper對象了: mLooper = Looper.myLooper();
同時從Looper中擷取到一個消息隊列,并且指派給Handler的本地的mQueque,我們在看一下Handler(Looper looper)這個構造方法如下:
/** * Use the provided queue instead of the default one. */ public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null; }
同樣也是接受使用者生成的一個Looper對象。是以是底層實作方式都是一模一樣了,從這裡我們也知道了為什麼預設情況下主線程都會預設的Looper對象去維護了。
2) 這裡我們需要在看一下為什麼會調用 Looper.myLooper();會擷取到一個Looper對象,跟蹤其源碼如下:
繼續跟蹤是誰給其sThreadLocal執行個體化/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
sThreadLocal 是從一個本地線程中擷取Looper類型的本地線程ThreadLocal對象,這裡隻需要明白ThreadLocal是一個Android提供管理線程的一個東西。// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在prepare()方法中,會從sThreadLocal通過get擷取一個本地線程的對象, 如果是空的話,這個東西中将new出來的Looper對象set到本地線程中。檢視ThreadLocal的get和set方法/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }
也就是說它終究是通過set的方式講new出來的Looper對象扔到ThreadLocal中,由它來完成初始化和關聯一個線程,如果要得到一個Looper對象就從ThreadLocal中get出來。通過這種方式來關聯和初始化指定線程的Looper對象。public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
1) MainActivity.java 中貼出onCreate()和 MyThread 類裡面的代碼段,讀者可以閱讀附件中的源代碼6. 在這裡為什麼添加完這兩個方法之後就會有Looper消息循環了?我們來檢視一下Looper的相關源代碼編譯執行發出異常:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initComponent(); new Thread(new MyThread()).start(); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // 點選按鈕的時候在UI主線程中向子線程發送消息 Message message = Message.obtain(); message.obj = "AHuier"; mHandler.sendMessage(message); } }); } public class MyThread implements Runnable { @Override public void run() { mHandler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); // 由于不能在子線程中更新UI,是以我們輸出到控制台. System.out.println("接受主線程中發出來的消息" + msg.obj); } }; } }
從這裡我們可以得出結論在子線程中,預設情況下是沒有Looper對象的,是以我們需要根據博文上面的Looper類的說明添加prepare()方法 和 loop()方法來啟動Looper消息循環。修改程式如下2)
2) 在MyThread子線程中添加prepare()方法 和 loop()方法完成Looper消息循環的啟動
程式執行結果:public class MyThread implements Runnable { @Override public void run() { Looper.prepare(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); // 由于不能在子線程中更新UI,是以我們輸出到控制台. System.out.println("接受主線程中發出來的消息" + msg.obj); } }; Looper.loop(); } }
1) prepare() 方法我們在上面已經知道,它會初始化目前的線程關聯一個Looper.
2) loop()源碼如下
它首先擷取Looper對象,然後将消息從Looper中取出,然後指派給MessageQueue,讓MessageQueue去管理,接着在While(true)這個死循環裡面一直在輪轉的取消息和分發消息(從Message msg = queue.next();和msg.target.dispatchMessage(msg);)這兩句代碼讀出。/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } long wallStart = 0; long threadStart = 0; // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); wallStart = SystemClock.currentTimeMicro(); threadStart = SystemClock.currentThreadTimeMicro(); } msg.target.dispatchMessage(msg); if (logging != null) { long wallTime = SystemClock.currentTimeMicro() - wallStart; long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadStart, threadTime); } } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } } }
三、總結與相關源碼
通過上述兩個Demo和Looper相關源碼的分析,我們可以知道Looper作為一個循環機制它的作用就是初始化線程和将Handler與該線程關聯的工作,以及管理,維護整個消息循環的機制。但是具體的發送消息還有處理消息都是靠Handler和Message來完成的。是以在一個新誕生的線程中,Looper都會關聯到這個Thread,以及它的MessageQueue和Handler.
源碼下載下傳:
http://download.csdn.net/detail/xukunhui2/6656293