本人,語言功底有限,本編可能是白話文。
Handler可能是大家學習Android的攔路虎,下面我將分析Handler的整個調用過程及我的總結。
本文大綱:
1.瞭解ThreadLocal類。
2.Handler Looper MessageQueue三者的代碼聯繫
3.關於回調的2種方法
4.HandlerThread的講解
5.其他好用的方法。
一,看這個類名大概就應該知道什麼,線程本地,改類主要提供了,線程本地(局部)存儲,存儲Looper對象,而真正存儲Looper對象的是Thread.Values類
該對象其實就是一個表格存儲這個對象,下面貼下源碼。
Looper類
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
ThreadLocal類
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
@SuppressWarnings("unchecked")
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);
}
Values values(Thread current) {
return current.localValues;
}
Thread類
ThreadLocal.Values localValues;
2.Handler Looper MessageQueue三者的代碼聯繫
首先上張圖片:
在理解上面那張圖片時,必須知道異步消息處理線程這個概念:線程啓動後會進入一個無限循環之中,每次循環都會讀取消息,並進行處理。
在Android中,異步消息處理線程可按照下面的格式來創建:
1、Looper.prepare() 創建一個消息隊列
2.消息體
3.Looper.loop() 讓該函數的線程進行無限循環。
下面我們來看源碼怎麼進入的:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}//如果重複調用該方法會報錯!!!!!所以說每個線程只能進行一次該方法
sThreadLocal.set(new Looper());
}
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();
}
}
}
public static Looper myLooper() {
return sThreadLocal.get();
}
現在我們看Handler 和上面的Looper類進行怎麼樣的消息交互和回調的。
在使用Handler時,大家一定很熟悉handler.sendEmptyMessage(msg);
但大家很少注意消息怎麼發送到handler所屬的線程和怎麼進行回調的,下面老規矩貼源碼:
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)//最終消息處理會到這個函數
{
boolean sent = false;
MessageQueue queue = mQueue;//大家好奇這個mQueue怎麼來的。看下面構造函數
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
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();//mLooper對象每個線程唯一,上面已經講了(該方法見下面)
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//大家看到了吧,和looper.loop循環題一直
mCallback = null;
}
Threadlocal類中
public static Looper myLooper() {
return sThreadLocal.get();
}
總結上面代碼:在非主線程中,如果Handler 不配合着和Looper.prepare(),Looper.loop()使用,在構造函數中將尋找不到mLooper和mQueue這2個變量,就不能像循環隊列發出消息,進行回調,當然也有別的辦法,列如下面的HandlerThread類。。。。
3.關於回調的2種方法
Handler的回調有2中方法:實現Callback接口和重寫handleMessage();優先級當然是接口高點
下面貼代碼
Handler myHandler=new Handler(this)
{
public void handleMessage(Message msg)
{
Log.v("two", "two");
}
};
@Override
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.v("one", "one");
return false;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
myHandler.sendEmptyMessage(1);
super.onWindowFocusChanged(hasFocus);
}
可以看到2個回調處理,當然當回調接口return true時,代表處理結束重寫的方法將不調用。
4.關於HandlerThread本人認爲是非常好的工具類。
看這個名字就知道繼承了Thread類,個人認爲與其他Thread類不同的地方有一個Looper對象
下面是代碼的用法:
HandlerThread thread = new HandlerThread(servName); thread.start(); Handler service = (Handler) clazz.getConstructor(Looper.class) .newInstance(thread.getLooper()); services.put(servName, service); service.sendEmptyMessage(SERVICE_START_MESSAGE_ID); HandlerThread重要源碼片段分析: public void run() { mTid = Process.myTid(); Looper.prepare();// synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop();//2個非常重要的方法 mTid = -1; } 可以看到已經在run方法創建了消息隊列和隊列循環工作了。 5。其他方法 handler.post(Runnable r)該方法是把r加到消息隊列,但並未開闢新線程。等到消息被取出時才執行。 Message m = Message.obtain(); m.obj = token; m.callback = r; return m;會把方法轉成消息。 Looper.quit()停止這個消息隊列。 Looper.myQueue().addIdleHandler(......)線程阻塞的時候調用,很管用。在主線程阻塞的情況下進行操作是很有必要的 如果覺得不錯,請好評。親。