編輯:關於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對象的認識。
結束。
Android聲音焦點----從音樂回到Luncher調節音量顯示的是Music的音量
聲音的類型有:定義在AudioSystem.java文件中 /* The default audio stream */public static final
Android 5.0中的CoordinatorLayout使用技巧
CoordinatorLayout 實現了多種Material Design中提到的滾動效果。目前這個框架提供了幾種不用寫動畫代碼就能工作的方法,這些效果包括:*讓浮動操
Android Studio 開發環境安裝筆記(圖文並茂windows版)
安裝Java開發工具包(JDK)的必要組件http://www.oracle.com/technetwork/java/javase/downloads/jdk8-dow
詳解Android更改APP語言模式的實現過程
一、效果圖二、描述更改Android項目中的語言,這個作用於只用於此APP,不會作用於整個系統三、解決方案(一)布局文件<LinearLayout xmlns:an