編輯:關於Android編程
上一篇文章,我們講到在調用Handler的sendMessage方法時,最終我們會進入到一個叫 sendMessageAtTime的方法,如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
從這裡,我們就會開始去探尋隱藏在 Handler對象後面的那一些我們想知道的實現機制了。
首先,我們還是從 Handler的創建開始說起,在上一篇文章,我們是通過 new Handler的方法來創建的,代碼如下:
private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_1: Log.v("Test", "Toast called from Handler.sendMessage()"); break; case MSG_ID_2: String str = (String) msg.obj; Log.v("Test", str); break; } } };
public Handler() { this(null, false); } public Handler(Callback callback) { this(callback, false); } public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } public Handler(boolean async) { this(null, async); } public Handler(Callback callback, boolean async) { 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 = callback; mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
public Handler(Callback callback, boolean async) { .... } public Handler(Looper looper, Callback callback, boolean async) { ... }
何謂線程相關的變量?就是線程間不能共享的對象,只在本線程內有作用的對象。
那麼Looper對象的作用是什麼?
從我個人的理解,Looper類就是對MessageQueue的封裝,它主要做的是兩件事:
1)構造Looper對象,初始化MessageQueue,我們可以從其構造函數看到:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
顯然,MessageQueue正是在創建Looper的時候初始化的。
我們還注意到,這個構造函數是private的,而它則是被Looper.prepare方法調用的,如下:public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
每一個線程中都只能有一個Looper對象。
2)調用loop()方法,循環處理消息,具體代碼如下:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; .... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... msg.target.dispatchMessage(msg); .... msg.recycle(); } }
在這裡,我們先不關心dispatchMessage方法,我們先跑一下題,看一下recycle方法裡面做了什麼事吧,如下:
private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message(); } public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
而在這裡,當調用recycle方法,就會將當前Message對象,先clearForRecycle之後,再添加到 sPool的頭部中,而當我們通過Message的obtain方法的時候,我們其實也是從sPool中拿 出一個空的Message對象。
相信看到這裡,大家就了解了上一篇文章中我說,為什麼建議大家使用Message.obtain方法去獲取消息對象了吧。
接下來,我們再回到正題上。
從上面關於Handler的創建和關於Looper的描述中,我們可以得出這樣一個結論:
在每一個線程中,如果我們要創建Handler,那麼此線程中就必須有一個Looper對象,而這個Looper對象中又封裝了一個MessageQueue對象來對Message進行管理。
所以,如果我們要在一個新線程中使用handler的話,我們就必須通過調用Loop.prepare()方法,為此線程創建一個Looper對象,官方給出的代碼如下:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
public class MainActivity extends ActionBarActivity { private static final int MSG_ID_1 = 1; private static final int MSG_ID_2 = 2; class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId()); switch (msg.what) { case MSG_ID_1: Log.v("Test", "Toast called from Handler.sendMessage()"); break; case MSG_ID_2: String str = (String) msg.obj; Log.v("Test", str); break; } } }; Looper.loop(); } } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId()); LooperThread looperThread = new LooperThread(); looperThread.start(); while(looperThread.mHandler == null){ } Message message = Message.obtain(); message.what = MSG_ID_1; looperThread.mHandler.sendMessage(message); Message msg2 = Message.obtain(); msg2.obj = "I'm String from Message 2"; msg2.what = MSG_ID_2; looperThread.mHandler.sendMessage(msg2); } }
對應的結果如下:
10-27 16:48:44.519: V/Test(20837): Id of MainThread : 1 10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421 10-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage() 10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421 10-27 16:48:44.529: V/Test(20837): I'm String from Message 2
當然,這肯定也是需要的,只是這一步是由Android系統幫我們做了,所以默認的主線程就不再需要去做這些初始化。
好了,這一篇文章,我們就了解了關於線程,Looper,Handler, MessageQueue 和 Message 等之間的一些關聯,而主要是對於Looper對象的認識。
結束。
聲音的類型有:定義在AudioSystem.java文件中 /* The default audio stream */public static final
CoordinatorLayout 實現了多種Material Design中提到的滾動效果。目前這個框架提供了幾種不用寫動畫代碼就能工作的方法,這些效果包括:*讓浮動操
安裝Java開發工具包(JDK)的必要組件http://www.oracle.com/technetwork/java/javase/downloads/jdk8-dow
一、效果圖二、描述更改Android項目中的語言,這個作用於只用於此APP,不會作用於整個系統三、解決方案(一)布局文件<LinearLayout xmlns:an