實際業務使用場景:
某業務場景需要將本地數據傳遞到服務端,服務端再返回傳遞成功或者失敗的信息。
1、 失敗時: 重傳5次
2、設置客戶端請求的最小時間間隔,這個間隔內最多請求1次
具體邏輯如下:(這里請求的最小時間間隔設置為:80s,防止客戶端由於某種異常頻繁的調用服務端,造成服務端異常)
實現:
handler 的實現:
public class BindHandler extends Handler {
private final static String TAG = "BindHandler";
private static final String DEFAULT_HANDLER_THREAD_NAME = "DeviceBindingHandlerThread";
static final int MSG_DEVICE_BIND = 1;
static final int MSG_DEVICE_BIND_RETRY = 2;
private BindManager mManager;
private HandlerThread mHandlerThread;
BindHandler(BindManager manager){
this(manager,createDefaultHandlerThread());
}
private BindHandler(BindManager manager,HandlerThread thread){
super(thread.getLooper());
mManager = manager;
mHandlerThread = thread;
}
private static HandlerThread createDefaultHandlerThread() {
HandlerThread handlerThread = new HandlerThread(DEFAULT_HANDLER_THREAD_NAME, Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
return handlerThread;
}
public void quitHandlerThread(){
if(null != mHandlerThread){
mHandlerThread.getLooper().quit();
}
}
public void startHandlerThread(){
if(null != mHandlerThread){
mHandlerThread.getLooper().loop();
}
}
public boolean isHandlerThreadAlive(){
return mHandlerThread.isAlive();
}
@Override
public void handleMessage(Message msg) {
if(null == msg){
return;
}
switch (msg.what){
case MSG_DEVICE_BIND:
mManager.handleBinding(true);
break;
case MSG_DEVICE_BIND_RETRY:
mManager.handleBinding(false);
break;
default:
break;
}
}
}
上述代碼中 設置了一個HandlerThread 用於開啟一個線程,請求的發送運行在該線程中(獲取該HandlerThread的Looper,用該Looper初始化Handler,這樣就可以將該Handler中handleMessage函數運行在開辟的線程中)。
通過Looper.quit函數結束該線程,否者該線程的Looper一直在循環等待消息的到來,占用資源(在HandlerThread已經失去價值的時候應該及時停掉它)。
方法1: mHandlerThread.getLooper().quit();//方法2: mHandlerThread.quit();方法3:mHandlerThread.getLooper().quitSafely();
//方法4:mHandlerThread.quitSafely();
1 和 2 結束的方法等同,不管消息隊列中有沒有消息都結束該線程。
3 和 4 中如果消息隊列中有消息,則不會立刻結束,待處理完消息隊列中的消息在結束該線程(處理過程中達到的消息不再處理)
請求管理類:
1、 請求管理類中定義自變量: mBindHandler,用於發送消息
2、設置重傳策略
3、設置防止頻繁調用的策略,(檢查消息隊列中是否有消息,如果有則丟棄該消息)
mBindHandler.hasMessages(BindHandler.MSG_DEVICE_BIND_RETRY)
具體代碼不再詳細分析
HandlerThread 使用詳解
/**HandlerThread(String name)
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public{HandlerThread(String name, int priority)
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public{public void run() {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
Looper.prepare();
mTid = Process.myTid();
喚醒等待線程
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //Looper.loop();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
getLooper()
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper{ //如果該線程沒有start 則處於等待狀態,當調用start后,notifyHandler getThreadHandler()
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait(); //線程沒有開啟則處於等待狀態
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public{quit()
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean{quitSafely()
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean{
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
從源碼可以看出HandlerThread繼續自Thread,構造函數的傳遞參數有兩個,一個是name指的是線程的名稱,一個是priority指的是線程優先級,我們根據需要調用即可。其中成員變量mLooper就是HandlerThread自己持有的Looper對象。onLooperPrepared()該方法是一個空實現,是留給我們必要時可以去重寫的,但是注意重寫時機是在Looper循環啟動前,再看看THread的run方法.
前面我們在HandlerThread的常規使用中分析過,在創建HandlerThread對象后必須調用其start()方法才能進行其他操作,而調用start()方法后相當於啟動了線程,也就是run方法將會被調用,而我們從run源碼中可以看出其執行了Looper.prepare()代碼,這時Looper對象將被創建,當Looper對象被創建后將綁定在當前線程(也就是當前異步線程),這樣我們才可以把Looper對象賦值給Handler對象,進而確保Handler對象中的handleMessage方法是在異步線程執行的。
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //喚醒等待線程
}
這里在Looper對象創建后將其賦值給HandlerThread的內部變量mLooper,並通過notifyAll()方法去喚醒等待線程,最后執行Looper.loop();代碼,開啟looper循環語句。那這里為什么要喚醒等待線程呢?我們來看看,getLooper方法
- HandlerThread本質上是一個線程類,它繼承了Thread;
- HandlerThread有自己的內部Looper對象,可以進行looper循環;
- 通過獲取HandlerThread的looper對象傳遞給Handler對象,可以在handleMessage方法中執行異步任務。
- 創建HandlerThread后必須先調用HandlerThread.start()方法,Thread會先調用run方法,創建Looper對象。
參考: http://blog.csdn.net/javazejian/article/details/52426353
Lopper.java 源碼分析
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) { // target為null,結束消息,結束循環
// No target is a magic identifier for the quit message.
return;
}
}
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
public void quit() {
Message msg = Message.obtain();
// NOTE: By enqueueing directly into the message queue, the message is left with a null target. This is how we know it is a quit message
// 發送一個target為null的消息,作為結束
mQueue.enqueueMessage(msg, 0);
}
我看到里面是一個無限循環,退出循環的條件是:msg.target == null;
也就是說,如果我們向此looper的MessageQueue發送一個target為null的message,就可以停止這個線程的遠行。(查看Looper.quit代碼可以驗證這一點)。
使用Looper.prepare()、Looper.loop()為Thread添加消息隊列后,該線程便開啟了。
使用總結
1. Looper類用來為一個線程開啟一個消息循環。
默認情況下android中新誕生的線程是沒有開啟消息循環的。(主線程除外,主線程系統會自動為其創建Looper對象,開啟消息循環。)
Looper對象通過MessageQueue來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。(如果對一個已經quit的Looper重新start會出現異常)
2. 通常是通過Handler對象來與Looper進行交互的。Handler可看做是Looper的一個接口,用來向指定的Looper發送消息及定義處理方法。
默認情況下Handler會與其被定義時所在線程的Looper綁定,比如,Handler在主線程中定義,那么它是與主線程的Looper綁定。
mainHandler = new Handler() 等價於new Handler(Looper.myLooper()).
Looper.myLooper():獲取當前進程的looper對象,類似的 Looper.getMainLooper() 用於獲取主線程的Looper對象。
3. 在非主線程中直接new Handler() 會報如下的錯誤:
E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原因是非主線程中默認沒有創建Looper對象,需要先調用Looper.prepare()啟用Looper。
4. Looper.loop(); 讓Looper開始工作,從消息隊列里取消息,處理消息。
注意:寫在Looper.loop()之后的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()后,loop才會中止,其后的代碼才能得以運行。
5. 基於以上知識,可實現主線程給子線程(非主線程)發送消息。

