前言:
之前的文章寫的都是關於Bitmap和內存的優化技術,這一篇文章給大家談談Handler。
Handler是Android系統中比較重要的一個知識,在Android多線程面試經常會被問到,在實際項目中的確也經常用到。當然也比較復雜,知識比較多,牽扯到的類有Thread、Looper、Message、MessageQueue。
Android是支持多線程的,通常應用程序中與用戶相關的UI事件都是運行在主線程中,比如點擊屏幕、按鈕等,為了保持主線程順暢相應用戶事件不被阻塞就需要把耗時的操作(主要是聯網、操作大文件等)放到子線程中,這個時候你可能會想到Handler(當然還你可以用其他的比如:異步任務,,這個以後再講),但是Handler又是怎麼和Thread聯系起來的呢?這個咱們來看一下Android主線程是怎麼創建的。
ActivityThread:
在ActivityThread.java中有一個main()函數,這個函數就是在一個應用啟動的入口,調用關系是:ActivityManagerService.java中的startProcessLocked函數調用如下代碼:
前言:
之前的文章寫的都是關於Bitmap和內存的優化技術,這一篇文章給大家談談Handler。
Handler是Android系統中比較重要的一個知識,在Android多線程面試經常會被問到,在實際項目中的確也經常用到。當然也比較復雜,知識比較多,牽扯到的類有Thread、Looper、Message、MessageQueue。
Android是支持多線程的,通常應用程序中與用戶相關的UI事件都是運行在主線程中,比如點擊屏幕、按鈕等,為了保持主線程順暢相應用戶事件不被阻塞就需要把耗時的操作(主要是聯網、操作大文件等)放到子線程中,這個時候你可能會想到Handler(當然還你可以用其他的比如:異步任務,,這個以後再講),但是Handler又是怎麼和Thread聯系起來的呢?這個咱們來看一下Android主線程是怎麼創建的。
ActivityThread:
在ActivityThread.java中有一個main()函數,這個函數就是在一個應用啟動的入口,調用關系是:ActivityManagerService.java中的startProcessLocked函數調用如下代碼:
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags,
app.info.targetSdkVersion, null);
Process.start又做了如下的操作,只看方法注釋就行,現在不需要知道具體做了什麼:
/**
* Start a new process.
*
* <p>If processes are enabled, a new process is created and the
* static main() function of a <var>processClass</var> is executed there.
* The process will continue running after this function returns.
*
* <p>If processes are not enabled, a new thread in the caller's
* process is created and main() of <var>processClass</var> called there.
*
* <p>The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
* make easily identifyable processes even if you are using the same base
* <var>processClass</var> to start them.
*
* @param processClass The class to use as the process's main entry
* point.
* @param niceName A more readable name to use for the process.
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int targetSdkVersion,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, targetSdkVersion, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
通過注釋也能看到上面的函數會找到ActivityThread的main函數並且執行。main函數中創建了Looper,Looper的作用就是利用線程創建一個消息處理隊列,並且維護這個消息隊列:
public static void main(String[] args) {
Looper.prepareMainLooper();//創建Looper
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);//應用所有的邏輯都在這個方法中
Looper.loop();//開啟一個消息循環,不斷的讀取MessageQueue中的Message。
}
Looper:
Looper.prepareMainLooper()的代碼如下:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
上面的方法注釋已經說的很明白,創建了主線程的Looper,這段代碼是系統調用的。先看prepare方法做了什麼操作。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();//獲取當前線程
}
創建主線程的Looper,每一個Looper對應一個Thread、一個MessageQueue,創建Looper的時候會創建一個MessageQueue。到目前位置創建了應用的主線程(Thread)、Looper、MessageQueue,調用Looper.loop(),開始不斷的從MessageQueue中讀取Message並處理,如果沒有消息則等待。現在有了消息循環,有了管理消息循環的Looper就差發送消息和處理消息的Handler了。
Handler:
這個時候你在你的應用中創建一個Handler,一般都是下面的代碼:
private static final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
..........
}
}
};
這個Handler是在主線程中創建的,Handler的構造函數如下:
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
mLooper = Looper.myLooper();//獲取上面在主線程創建的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//獲取Looper的MessageQueue
mCallback = null;//默認為null在後面處理msg時會就行檢查
}
創建完Handler你就可以用了,比如你發一個消息:
Java
mHandler.sendEmptyMessage(MSG_WHAT);
在系統中會走最終走到Handler.java下面的方法:
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//注意這行代碼後面會用,把Handler賦值給Msg的target對象
sent = queue.enqueueMessage(msg, uptimeMillis);//把msg放到MsgQueue中
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
上面的方法第二個是延時毫秒數,queue.enqueueMessage把消息發送到MessageQueue後剩下的就是等待消息被處理,前面不是說了Looper.loop()方法開始輪詢消息隊列嗎,你發送的消息就是在loop方法中讀取到的,讀取到後誰去處理呢?在loop()方法中有一句代碼:
msg.target.dispatchMessage(msg);
msg就是你發送到MessageQueue的消息,被讀取後調用target.dispatchMessage(),這個target就是上面Handler發送消息是賦值的,就是發送消息的Handler本身,然後Handler調用自己的下面方法就行消息處理:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//在這會調用到上面重寫的handleMessage方法。
}
}
因為在new Message的時候callback為空,並且Handler的mCallback = null,所以會調用到你上面new Handler時重寫的handleMessage方法。
總結:
每一個線程中都對應一個Looper,每一個Looper都對應一個MessageQueue,這個Looper是用來管理消息隊列的,主要是讀取消息隊列和把消息發送給Message的target去處理。到這你應該清除Thread、Handler、Message、MessageQueue和Looper他們之間的關系了吧。
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11