編輯:關於Android編程
出於帶著問題找答案的思想,我打算先收集一些面試題,然後再帶著這些問題去源碼中找答案,最後自己給出答案。
有幾種可以選擇:
第一種,由傳入的Callback,Looper構造
- public Handler()
- public Handler(Callback callback)
- public Handler(boolean async)
他們對應這個方法
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class 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; }
下面對這個構造函數所做的事進行分析
首先出現在眼前的就是這個變量 FIND_POTENTIAL_LEAKS,這個變量默認為false,且為private類型,如果要使用到它,只有通過匿名內部類,本地類,成員類等。為的是detect?這是啥意思,繼續往下看
Set this flag to true to detect anonymous, local or member classes
that extend this Handler class and that are not static. These kind
of classes can potentially create leaks.
進行判斷的目的是如果這個handler屬於匿名類,本地類,成員類或者非靜態類,就會報出這個警告。
為什麼要對這幾種類加以區分呢?
然後就是調用mylooper來獲取looper,如果looper為空就報錯,可以看到這裡調用的looper持有了MessageQueue,這就是三者的關系。
接下來看看獲取looper的過程
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
這裡的sThreadLocal是looper的一個成員變量。通過prepare來實現
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)); }
這裡將sThreadLocal裡面的looper進行了設置。這裡就可以解釋為什麼在次線程中需要先prepare再使用handler了,因為不這樣get不到looper
至於主線程的,我認為應該是在ActivityThread裡面進行了prepare,這點讓我在後面的時候再去查詢。
第二種,由傳入的Looper構造
- public Handler(Looper looper)
這種方法應該是對應於次線程的。它對應於這個方法
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
只是把looper換了一下而已。沒啥大不了的
至於callback的作用,我不是很理解,而且我看到了這樣的代碼
public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
這裡的兩個handleMessage,第二個是在定義handler的時候用的,用來處理當前線程傳來的數據,至於第一個callback,可以由activity或service來實現,它能接收到數據,如下
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
可以看到只有這一個地方調用所有的handleMessage,所以推斷只有這一個地方進行事件的分發處理。並且它的順序是先調用callback的handleMessage再調用自己的handleMessage
有三種方法可以創建message
1.msg=Message.obtain(handler)
2.msg=handler.obtainMessage();
3.msg=new Message();
主要分析一下前面兩種方法
1.Message.obtain()方法:可以看到源碼中有很多的這個實現,但是所有的實現都是基於這個方法
/** * 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; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
可以看到它的實現比起單純的new Message來說可以避免無意義的分配內存。sPool是一個Message實例,sPoolSize初始為0
在這裡進行了Message的回收工作。
/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
查詢這個方法的調用情況可知,這個方法應該是用於將一些使用過的Message進行重新利用。到了具體情況再進行分析
至於handler.obtainMessage()方法,可以看到全是調用了Message.obtain()方法。至於後面用了的message會怎麼樣,感覺應該屬於MessageQueue的范疇,暫時不看。
這裡給出了兩個方法來發送消息,也是異步消息機制的兩種
- post(Runnable r)
- sendMessage()
這是post的源碼,postDelayed之類的都是基於一個源碼,getPostMessage就是通過Message.obtain()獲得的,將Runnable對象打包進去作為了message的參數,返回了一個Message對象。
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
這是sendMessage()的源碼
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
可以看到都是基於sendMessageDelayed方法。下面我們來分析一下這個方法,看看到底是怎麼進行信息的發送的。應該也會與Looper和MessageQueue進行交互。
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); }
我們繼續來跟蹤enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
可以看到,最終的結果就是將這條Message加入到了綁定的MessageQueue上面。
總結,handler方法其實就是綁定Looper和MessageQueue,通過Message.obtain來獲得message,通過將message加入到queue中來結束這次異步處理。
待解決問題:使用完的message怎麼樣了?是怎麼樣進行再次利用的?
Looper的構造函數是private的,只能通過prepare來使用
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } 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)); }
這個sThreadLocal是關鍵,下面來看看他的性質
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
可以看到這個類最適合放一些只有這個線程才能持有的元素。
總結,Looper類主要的作用就是持有MessageQueue對象。它通過prepare來將自己放入ThreadLocal中,通過myLooper來將自己取出來綁定Handler。但是這個類的作用是什麼呢?就是單純的持有MessageQueue嗎?如果真是這樣的話為啥不直接把MessageQueue放到Handler中去?
而且我在這裡看到了這樣的一個方法
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.recycleUnchecked(); } }
這樣就清楚了,就是這個Looper進行message的調控工作,不斷的進行循環將message從queue中取出並進行dispatchMessage,然後將用完的message進行recycleUnchecked即回收利用。這樣我們就可以回答前面的Handler章最後的那個問題了
就是這個方法
void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
將所有參數清空,並且將這個message放入pool中,只不過我現在還沒有見到這個pool的影子,但是事實上他是通過這個靜態變量來實現的。而且他這個pool是倒著用的。這樣的一幅圖示
A<————-B <—————— C A先加入,C最後。
MessageQueue中有兩個重要的方法:next和enqueueMessage
synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; }
最後會返回這個得到的message
enqueueMessage:
synchronized (this) { if (mQuitting) { msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
總結一下就是MessageQueue作為一個存儲的util來用。提供next和enqueueMessage方法就行了。
第一次看到這玩意,我嚇傻了,這是什麼,好像從來沒見過。後來查了一下他的源碼和官網介紹,其實只是一個簡化合成版的Handler-Looper-MessageQueue機制而已,便於使用。
ThreadLocal很容易讓人望文生義,想當然地認為是一個“本地線程”。其實,ThreadLocal並不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。
當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單:在ThreadLocal類中有一個static聲明的Map,用於存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。
public class ThreadLocal {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
protected T initialValue() {
return null;
}
public ThreadLocal() {
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
T childValue(T parentValue) {
throw new UnsupportedOperationException();
}
static class ThreadLocalMap {
static class Entry extends WeakReference {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
ThreadLocal key = e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
private void replaceStaleEntry(ThreadLocal key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
int slotToExpunge = staleSlot;
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i;
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
private void rehash() {
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis
if (size >= threshold - threshold / 4)
resize();
}
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}
}
}
結論如下:
1. ThreadLocal只是操作Thread中的ThreadLocalMap對象的集合.
2. ThreadLocalMap變量屬於線程的內部屬性,不同的線程擁有完全不同的ThreadLocalMap變量.
3. 線程中的ThreadLocalMap變量的值是在ThreadLocal對象進行set或者get操作時創建的.
4. 使用當前線程的ThreadLocalMap的關鍵在於使用當前的ThreadLocal的實例作為key來存儲value值.
5. ThreadLocal模式至少從兩個方面完成了數據訪問隔離,即縱向隔離(線程與線程之間的ThreadLocalMap不同)和橫向隔離(不同的ThreadLocal實例之間的互相隔離).
面試題回答
Handler機制是什麼?原理是什麼?
答:handler機制是handler-looper-MessageQueue的結合機制,原理是handler綁定該線程裡面的looper,依靠looper裡面的MessageQueue,可以將消息都發送到這個MessageQueue中,looper進行不斷取出MessageQueue裡面的消息,分發給handler的實例和callback來處理。
HandlerThread是什麼?
答:一個簡化的handler-looper-MessageQueue模型,方便使用。
用完以後的message是什麼時候如何回收的?
在Looper的loop方法中,消息用完馬上使用recycleUnChecked方法回收。而looper主要的處理工作就是調用msg.target.dispatchMessage()方法來分發出去處理。
android的線程模型
UI線程不能做耗時的工作,需要其他線程來做並將結果返回UI線程,有三種機制可以幫忙
Handler view.post(Runnable) runOnUithread AsyncTask
MessageQueue是什麼時候創建的?
答: 是在prepare的時候,prepare的時候把Looper和MessageQueue都為後面的工作准備好了
ThreadLocal在Handler機制中的作用
保存這些looper
Handler的post方法原理?
將post中的Runnable對象作為一個message的參數傳進去構造一個message,返回這個message對象來
Handler導致內存洩露問題?
通常,我們在開發過程中,總是需要兩套以上的環境進行測試、生產發布。如果只是簡簡單單的進行一個API的切換,那麼只是進行不同的宏定義即可,但是要求應用的不同版本將使用相同的
硬件平台:S3C6410 操作系統:Ubuntu、windows 板子系統:Android 開發工具:jdk,ndk,eclipse 本次測試從linux內核模塊編譯開始
一、訪問網絡資源1、使用統一資源定位符獲取網絡資源的路徑urlURL url = new URL(path);2、通過url打開網絡連接(連接方式有http連接,ftp連
WhirlyGlobe-Maply是一個基於OpenGL ES、專注移動應用的開源地圖工具包,支持ios和android平台,本文以android平台為例。環境:wind