Ⅰ android Handler那些事兒,消息屏障IdelHandlerANR
Handler 是Android SDK中用來處理非同步消息的核心類,子線程可以通過handler來通知主線程進行ui更新。
備註:本文源碼截圖 基於Android sdk 28
Handler機制 消息發送主要流程如圖
應用程序啟動後,zygote fork一個應用進程後,和普通java程序一樣,程序會首先執行ActivityThread中的main函數。在main函數中,程序首先會創建Looper對象並綁定到主線程中,然後開啟loop循環。(ps:主線程loop循環不能退出)
在prepareMainLooper方法中,最終會創建Looper,MessageQueue對象 以及創建native層MessageQueue對象。
使用Handler.sendMessageXXX或這 postDedayXXX發送消息後,最終會調用到SendMessageAtTime方法中。
然後調用MessageQueue.enqueueMessage將消息存到消息隊列中。
存入消息後,然後通過調用native方法 喚醒主線程進行消息處理。
當應用程序啟動,做完一些必要工作之後,便會開啟Loop循環,除非系統異常,否則該循環不會停止。loop循環中,主要做兩件事,第一,從消息隊列中取消息。第二,進行消息分發處理。
MessageQueue.next() 方法 通過調用 native方法 nativePollOnce(ptr, nextPollTimeoutMillis)實現無消息處理時,進入阻塞的功能。
當nextPollTimeoutMillis 值為0時,該方法會立刻返回;
當nextPollTimeoutMillis 值為-1時,該方法會無限阻塞,直到被喚醒;
當nextPollTimeoutMillis 值大於0時,該方法會將該值設置為超時時間,阻塞到達一定時間後,返回;
在loop循環中 ,通過調用 msg.target.dispatchMessage(msg) 進行消息的分發處理
使用當前線程的MessageQueue.addIdleHandler方法可以在消息隊列中添加一個IdelHandler。
當MessageQueue 阻塞時,即當前線程空閑時,會回調IdleHandler中的方法;
當IdelHandler介面返回false時,表示該IdelHandler只執行一次,
a,延遲執行
例如,當啟動Activity時,需要延時執行一些操作,以免啟動過慢,我們常常使用以下方式延遲執行任務,但是在延遲時間上卻不好控制。
其實,這時候使用IdelHandler 會更優雅
b,批量任務,任務密集,且只關注最終結果
例如,在開發一個IM類型的界面時,通常情況下,每次收到一個IM消息時,都會刷新一次界面,但是當短時間內, 收到多條消息時,就會刷新多次界面,容易造成卡頓,影響性能,此時就可以使用一個工作線程監聽IM消息,在通過添加IdelHandler的方式通知界面刷新,避免短時間內多次刷新界面情況的發生。
在Android的消息機制中,其實有三種消息: 普通消息、非同步消息及消息屏障。
消息屏障 也是一種消息,但是它的target為 null。可以通過MessageQueue中的postSyncBarrier方法發送一個消息屏障(該方法為私有,需要反射調用)。
在消息循環中,如果第一條消息就是屏障消息,就往後遍歷,看看有沒有非同步消息:
如果沒有,則無限休眠,等待被喚醒
如果有,就看離這個消息被觸發時間還有多久,設置一個超時時間,繼續休眠
非同步消息 和普通消息一樣,只不過它被設置setAsynchronous 為true。有了這個標志位,消息機制會對它有些特別的處理,我們稍後說。
所以 消息屏障和非同步消息的作用 很明顯,在設置消息屏障後,非同步消息具有優先處理的權利。
這時候我們回顧將消息添加到消息隊列中時,可以發現,其實並不是每一次添加消息時,都會喚醒線程。
當該消息插入到隊列頭時,會喚醒該線程;
當該消息沒有插入到隊列頭,但隊列頭是屏障,且該消息是隊列中 靠前的一個非同步消息,則會喚醒線程,執行該消息;
調用MessageQueue.removeSyncBarrier 方法可以移除指定的消息屏障
ANR 即 Application Not Response, 是系統進程對應用行為的一種監控,如果應用程序沒有在規定時間內完成任務的話,就會引起ANR。
ANR類型
Service Timeout : 前台服務20s, 後台服務200s
BroadcastQueue Timeout : 前台廣播 10s,後台廣播60s
ContentPrivider Timeout : 10s
InputDispatching Timeout : 5s
比如,在啟動一個服務時, AMS端通過應用進程的Binder對象創建Service, 在scheleCreateService()方法中 會調用到當前service的onCreate()生命周期函數;
bumpServiceExecutingLocked()方法內部實際上會調用到scheleServiceTimeoutLocked()方法,發送一個ActivityManagerService.SERVICE_TIMEOUT_MSG類型消息到AMS工作線程中。
消息的延時時間,如果是前台服務,延時20s, 如果是後台服務,延時200s;
如果Service的創建 工作在 上訴消息的延時時間內完成,則會移除該消息,
否則,在Handler正常收到這個消息後,就會進行服務超時處理,即彈出ANR對話框。
復雜情況下,可能會頻繁調用sendMessage 往消息隊列中,添加消息,導致消息積壓,造成卡頓,
1,重復消息過濾
頻繁發送同類型消息時,有可能隊列中之前的消息還沒有處理,又發了一條相同類型的消息,更新之前的數據,這時候,可以採用移除前一個消息的方法,優化消息隊列。
2,互斥消息取消
在發送消息時,優先將消息隊列中還未處理的信息已經過時的消息 移除,優化隊列
3,隊列優化-復用消息
創建消息時,優先採用之前回收的消息,避免重復創建對象,引起GC
完~
(如果錯誤或不足,望指出, 大家共同進步)
Ⅱ 如何關閉android中的HandlerThread
android 中的HandlerThread包含了android中的消息處理機制必須的looper,當你啟動這個線程的時候,就會闖進looper,並開啟消息處理的循環。
跟其它線程一樣,HandlerThread是可不可以直接stop掉的,不過經過本人測試,你可以調用:getLooper().quit();來退出這個線程,其實原理很簡單,就是改變在消息循環裡面標志位,退出整個while循環,使線程執行完畢。
部分 測試代碼如下:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv_name = (TextView) findViewById(R.id.tv_name);
btn_name = (Button) findViewById(R.id.btn_name);
Log.i(TAG, "==================main thread:" + Thread.currentThread().getName());
final HandlerThread thread = new HandlerThread("handlerThread");
thread.start();
final MyHandler handler = new MyHandler(thread.getLooper());
btn_name.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {
handler.sendEmptyMessage(1);try {Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();}thread.getLooper().quit();}});}
class MyHandler extends Handler {
public MyHandler(Looper looper) {
super(looper);}@Overridepublic void handleMessage(Message msg) {
Log.i(TAG, "==================" + Thread.currentThread().getName());
//tv_name.setText("hello, this is the first message from handler");
不過要注意:要想更新界面內容,還是需要使用界面的Looper,不然的話還是會拋錯誤,還是那句話,所有跟改變界關的操作,都得通過界面的Looper來執行
Ⅲ android handler是不是唯一的
前段時間在工作的時候碰見一個問題,就是要待機時在設定的時間內執行操作,開始並沒有意識到問題的嚴重,後來嘗試很多辦法沒有成功,最後也是在網上找到解決辦法,在此稍作總結,希望能對大家有所幫助,不足之處還望大家指正。
1Android中的handler、timer、thread、在待機時都會停止運行,所設定的時間會在待機結束後繼續計算。所以如果想在Android待機時運行某些操作,使用以上幾種方法是不可行的。
2Android中有一個Alarmmanager對象,可以使用該對象執行待機時的操作。具體設置的方法如下:
2.1設置鬧鈴的類型
AlarmManager.RTC,硬體鬧鍾,不喚醒手機(也可能是其它設備)休眠;當手機休眠時不發射鬧鍾。
AlarmManager.RTC_WAKEUP,硬體鬧鍾,當鬧鍾發躰時喚醒手機休眠;
AlarmManager.ELAPSED_REALTIME,真實時間流逝鬧鍾,不喚醒手機休眠;當手機休眠時不發射鬧鍾。
AlarmManager.ELAPSED_REALTIME_WAKEUP,真實時間流逝鬧鍾,當鬧鍾發躰時喚醒手機休眠;
AlarmManager.POWER_OFF_WAKEUP:能喚醒系統,他是一種關機鬧鈴,就是說設備在關機狀態下也可以喚醒系統,所以我們把它稱為關機鬧鈴。
RTC鬧鍾和ELAPSED_REALTIME最大的差別就是前者可以通過修改手機時間觸發鬧鍾事件,後者要通過真實時間的流逝,即使在休眠狀態,時間也會被計算。
2.2設置鬧鈴的開始時間
如果使用ELAPSED_REALTIME或者ELAPSED_REALTIME_WAKEUP類型應該調用SystemClock.elapsedRealtime()獲取相對時間在加上你設定的延遲時間
如果使用RTC或者RTC_WAKEUP類型應該調用System.currentTimeMillis()獲取從1970.1.1號以來的時間在加上你設定的延遲時間
2.3pendingintent
一個PendingIntent對象,表示到時間後要執行的操作。PendingIntent與Intent類似,可以封裝Activity、BroadcastReceiver和Service。
但與Intent不同的是,PendingIntent可以脫離應用程序而存在。
接觸Android沒幾天,不太了解。
本來寫好的一個應用在無意中發現,待機的時候,應用中的一個線程停止了運行。
這個線程是每隔一分鍾上傳一個數據到伺服器上。
我當時測試的時候,沒想過待機(接開關鍵)下的情況是怎樣的,現在發現,只要手機一進入待機狀態,這個線程就停止工作了。
不過有一個奇怪的現象,因為我的應用中同時啟動了三個線程。
一個負責每隔一分鍾上傳一個數據,當待機的時候,這個線程暫停運行,當手機不待機的時候,馬上復活。
一個負責接收伺服器發過來的UDP數據包,這個線程倒是不受待機的影響,當有數據來的時候,可以正常處理。
難道是因為datagramSocket.receive(datagramPacket);阻塞的原因?
public void run()
{
while(true)
{
datagramSocket.receive(datagramPacket); //阻塞
}
}
到網上搜索了一下,看到別人說的:
實驗1:使用Java.util.Timer
當連接USB線進行調試時,會發現一切工作正常,每5秒更新一次界面,即使是按下電源鍵,仍然會5秒觸發一次。
當拔掉USB線,按下電源鍵關閉屏幕後,過一段時間再打開,發現定時器明顯沒有繼續計數,停留在了關閉電源鍵時的數字。
實驗2:使用AlarmService:
2.1通過AlarmService每個5秒發送一個廣播,setRepeating時的類型為AlarmManager.ELAPSED_REALTIME。
拔掉USB線,按下電源鍵,過一段時間再次打開屏幕,發現定時器沒有繼續計數。
2.2setRepeating是的類型設置為AlarmManager.ELAPSED_REALTIME_WAKEUP
拔掉USB線,按下電源鍵,過一點時間再次打開屏幕,發現定時器一直在計數。
如此看來,使用WAKEUP才能保證自己想要的定時器一直工作,但是肯定會引起耗電量的增加。
我最後自已寫了一個Service類,然後使用AlarmService每隔一分鍾執行一次,在待機的時候也能正常運行。
Ⅳ 如何終止Android Handler 中的消息處理
終止Android Handler 中的消息處理的辦法:
首先你可以放到線程中去執行,這個應該是個耗時操作,放到UI線程中,程序會假死。
然後你可以通過handler去啟動這個線程來執行這個方法,如果取消的話,在發給handler一個消息,讓handler再去停止這個線程。
Ⅳ Android的handler機制的原理
Android的handler機制的原理分為非同步通信准備,消息發送,消息循環,消息處理。
1、非同步通信准備
在主線程中創建處理器對象(Looper)、消息隊列對象(Message Queue)和Handler對象。
2、消息入隊
工作線程通過Handler發送消息(Message) 到消息隊列(Message Queue)中。
3、消息循環
消息出隊: Looper循環取出消息隊列(Message Queue) 中的的消息(Message)。
消息分發: Looper將取出的消息 (Message) 發送給創建該消息的處理者(Handler)。
4、消息處理
處理者(Handler) 接收處理器(Looper) 發送過來的消息(Message),根據消息(Message) 進行U操作。
handler的作用
handler是android線程之間的消息機制,主要的作用是將一個任務切換到指定的線程中去執行,(准確的說是切換到構成handler的looper所在的線程中去出處理)android系統中的一個例子就是主線程中的所有操作都是通過主線程中的handler去處理的。
Handler的運行需要底層的 messagequeue和 looper做支撐。
Ⅵ android 關於Handler終止問題
你的判斷語句有問題,相當於移除了消息又添加了消息,加一個else應該就能解決。
if(progress>=stop){
mHandler.removeMessages(MSG_PROGRESS_UPDATE);
}else{
mHandler.sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE,30);
}
Ⅶ android應用關閉了 銷毀了 但是fragment里邊的handler還在運行 怎麼回事
1、不要直接將Fragement寫在XML文件里,這樣會導致Fragment對象無法銷毀,產生內存溢出。在代碼動態的new 和add Fragement才是正確的做法。 2、使用抽屜方法,在XML文件里放兩個LinearLayout,然後在代碼中將Fragment加進這兩個LinearLayout中
Ⅷ 安卓定時器Handler如何關閉
{
publicTimeCounts(longmillisInFuture,longcountDownInterval){
super(millisInFuture,countDownInterval);//參數依次為總時長,和計時的時間間隔
}
@Override
publicvoidonFinish(){//計時完畢時觸發
}
@Override
publicvoidonTick(longmillisUntilFinished){//計時過程顯示
//倒計時數可以寫在這兒
}
}
TimeCounts timeCounts = new TimeCounts(30000, 1000);//總時長30s,間隔1s
timeCounts.start();//倒計時開始
timeCounts.cancel();//取消倒計時
Ⅸ android中,怎麼停止handler
Android四大組件如人體四肢,Handle如人體中的血液。它貫穿於各個組件,連Activity的onCreat方法都是由它調用的。你讓它停下來?我真的很好奇: 你的程序怎麼跑???