Android Hanlder機制分析(一) Hanlder常見用方法及原理
一、Handler介紹
在android開發的過程中,經常需要對UI進行更新操作。但是在子執行緒中對UI進行更新在android中是不允許的,這涉及到了執行緒安全問題,因此更新UI只能在主執行緒(UI執行緒)中進行。但是如果將耗時操作放在了UI執行緒中會帶來程式體驗上卡頓甚至ANR等問題。然而通過Android的Handler機制能夠很好解決此類問題。
Handler為UI的非同步跟新提供了一個很好的方式。Handler的主要作用是在子執行緒中進行耗時操作,將得到的資料通過handler 傳送個主執行緒(UI執行緒)主執行緒根據結果進行UI的更新。
二、Handler相關方法介紹
在使用Handler的時,通常需要將Handler訊息傳送給主執行緒,常用的方法如下:
sendEmptyMessage(int what):傳送空訊息
boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之後傳送空訊息
sendMessage(Message msg):立即傳送訊息
sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之後傳送訊息
obtainMessage():從訊息佇列中返回一個Message
sendMessageAtFrontOfQueue(Message msg):將message插入到messageQueue前列。
方法還有很多,此處不一一列舉。
三、Hanlder使用例項
在使用Hanlder的時候通常需要以下幾個步驟;
private Handler mHandler;
mHandler = new Handler() {
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
switch(msg.what) {
case XX:
需要執行的程式碼
break ;
case XX:
需要執行的程式碼
break;
default:
break;
}
}
};
Message msg = new Message();
msg.what = XXX;
mhandler.sendMessage();
總的說來分為了三步,先新建一個Handler的例項,再根據需求重寫了handleMessage(Message msg)方法,最後通過mHandler傳送訊息交由handleMessage(Message msg)處理。
四 Handler實現原理
Handler的實現與三個元件相關:
1.Message:Handler接收和處理的訊息物件
2.Looper:每個執行緒只能擁有一個Looper.它的loop方法負責讀取MessageQueue中的訊息,
讀到資訊之後就把訊息交給傳送該訊息的Handler進行處理。
3.MessageQueue:訊息佇列,它採用先進先出的方式來管理Message.
在一個activity中,程式自動的幫我們綁定了一個loop到主執行緒。在ActivityThread.java可以看到:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
......
// Make sure TrustedCertificateStore looks in the right place for CA certificates
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
.......
}
因此,我們可以在UI執行緒中直接使用Handler。如果需要在自己執行緒中使用handler 則需要自己向執行緒繫結looper,具體實現在後面文章中詳細解釋。
現在我們來先看看 Looper,Message ,Handler他們之間的關係。
Message:訊息的載體。可以攜帶訊息種類、資料等資訊。
(1)public int what:變數,用於定義此Message屬於何種操作
(2)public Object obj:變數,用於定義此Message傳遞的資訊資料,通過它傳遞資訊
(3)public int arg1:變數,傳遞一些整型資料時使用
(4)public int arg2:變數,傳遞一些整型資料時使用
Looper:每一個執行緒只能有一個Looper,它負責這從MessageQueue中取出Message並將其分發給Handler處理。下面來看看Looper是怎麼執行的。
public final class Looper {
private static final String TAG = "Looper";
......
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
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));//
}
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;
......
/*迴圈裡,不斷從MessageQueue中取出msg ,根據msg所攜帶的資訊進行分發*/
for (;;) {
Message msg = queue.next(); // 取出msg
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 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);
}
msg.target.dispatchMessage(msg);//分發訊息給handler 處理
......
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Handler 類主要用於處理自己發出的msg,而msg又在loop中進行取出。Handler原始碼如下:
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();//handler與looper繫結
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//handler與msgqueue繫結
mCallback = callback;
mAsynchronous = async;
}
綜上,在一個執行緒中綁定了一個looper後即成為了Looper執行緒。這looper執行緒中,可以新建Handler ,再通過handler例項向messageQueue中放入訊息,放入的訊息由looper取出再分發給相應的Handler處理。
(下一篇將寫怎麼去自建一個looperThread ,HanderThread ,以及hander.obtain()的優點)。