0.Thanks
性能優(yōu)化十六之Wake_Lock喚醒鎖以及JobScheduler使用
安卓 java 判斷socket斷開
android保持服務(wù)不休眠(持續(xù)運行)以及喚醒屏幕的方法
Android API 19 及以上版本AlarmManager setRepeating 不準或只執(zhí)行一次的解決方案
1.概述
前陣子接到一個用戶反饋說昔园,公司的一款機器人备恤,很容易掉線炊豪。這款產(chǎn)品上稿辙,跑的是Android5.1系統(tǒng)葛超,用的是Socket與自己服務(wù)器保持著長鏈接。于是開始排查問題浅辙。删顶。。
我們知道闭专,一般我們在客戶端上使用心跳協(xié)議奴潘,跟服務(wù)器保持著長連接。所以影钉,先從這里下手画髓,懷疑是服務(wù)器心跳協(xié)議那段代碼有問題。然后實際上不是平委。
后面繼續(xù)找奈虾,發(fā)現(xiàn)了幾個問題:
問題1,心跳包是客戶端發(fā)送的廉赔,而服務(wù)器收到心跳包是沒有回包的肉微,這樣會存在一個情況,有時候因網(wǎng)絡(luò)原因蜡塌,心跳包一直有發(fā)送碉纳,但客戶端并不知道是否成功,而使得socket鏈接被服務(wù)器判定為超時岗照,就被服務(wù)器斷開了村象。
問題2笆环,接著上面的問題攒至,socket被服務(wù)器判斷為超時厚者,服務(wù)器主動斷開socket,但是迫吐,客戶端并沒有收到fin包库菲,也就是socket斷開的信號。為啥沒有收到4次揮手志膀,嗯熙宇,有機會要深究。
問題3溉浙,發(fā)送心跳包是有間隔的烫止,并不是一支持續(xù)地發(fā)送,但從客戶端本地的日志來看戳稽,發(fā)現(xiàn)在鎖屏的時候馆蠕,心跳包的發(fā)送并不是按照程序那樣子去跑。程序?qū)懙氖敲?分鐘跑一次心跳惊奇,但是互躬,鎖屏后,這個時間就不是5分鐘颂郎,有可能是10分鐘
2.解決問題1:修改心跳協(xié)議
心跳協(xié)議是讓雙方知道吼渡,雙方都在線,既然是雙方乓序,心跳也應(yīng)該是有回包的寺酪。所以,后面把心跳協(xié)議改成替劈,服務(wù)器收到心跳包寄雀,也應(yīng)該回一個收到心跳包的通知√е剑考慮到有時候咙俩,會因為網(wǎng)絡(luò)的緣故,會丟包湿故,所以阿趁,在客戶端上增加重試的邏輯。所以現(xiàn)在變成坛猪,先發(fā)送心跳包脖阵,在N秒內(nèi)若沒有收到回復(fù),則再發(fā)送一次墅茉。重試M次命黔。
3.解決問題2:判斷socket是否已經(jīng)斷開了
1)isClosed()呜呐、isConnected()、isInputStreamShutdown()悍募、isOutputStreamShutdown()方法來判斷
- 這種方式只是本地判斷蘑辑,只是本地操作connect()或close()方法后保存的一個狀態(tài),
對于遠程服務(wù)器主動斷開就沒有用了
2)sendUrgentData()判斷遠程服務(wù)器是否斷開Socket
往輸出流發(fā)送一個字節(jié)的數(shù)據(jù)坠宴,只要對方Socket的SO_OOBINLINE屬性沒有打開洋魂,就會自動舍棄這個字節(jié),SO_OOBINLINE屬性默認情況下就是關(guān)閉的喜鼓!
但是副砍,經(jīng)測試,這個方法行不通庄岖。但網(wǎng)上說這個方法有用豁翎,唉,可能我的是假socket隅忿。
3)通過OutputStream.write
- 有人說心剥,發(fā)送心跳協(xié)議的時候,try catch一下硼控,如果有報異常刘陶,就說明是斷開了。實際并不然牢撼,實際測試中匙隔,
服務(wù)器斷開socket,還是可以發(fā)包熏版,并且不會報異常
既然以上的辦法都無法判斷纷责,只能依靠心跳協(xié)議了,只要服務(wù)器沒有按心跳的邏輯回包撼短,就判斷已經(jīng)斷開并主動去鏈接再膳。實際測試中,修該后的心跳協(xié)議曲横,是可以很好地保持長鏈接喂柒。
4.解決問題3:系統(tǒng)休眠定時器不準
系統(tǒng)鎖屏后一段時間,系統(tǒng)會進入一個休眠狀態(tài)禾嫉,此狀態(tài)下灾杰,系統(tǒng)會把線程掛起,所以Timer定時器會不準確熙参。
清楚問題了艳吠,就好解決∧跻可以通過以下解決:
1)準確定時
- 系統(tǒng)休眠導(dǎo)致定時器不準備昭娩,那有沒有辦法可以讓定時器正常工作呢凛篙?有!
在API19栏渺,可以這樣做呛梆,親測起效,還有迈嘹,你還需監(jiān)聽此Action的廣播:
/**
* 設(shè)置定時器
* @param timeMs 毫秒削彬,既是全庸,多少毫秒后秀仲,觸發(fā)
* @param action 觸發(fā),以廣播形式壶笼,傳入廣播的action
*/
private void setAlarm(int timeMs,String action) {
try {
AlarmManager am = (AlarmManager) XHoneyBotApplicationLike.getInstance().getApplication().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(action);
PendingIntent sender = PendingIntent.getBroadcast(XHoneyBotApplicationLike.getInstance().getApplication(), 0, intent,PendingIntent.FLAG_CANCEL_CURRENT);
if (am!=null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//參數(shù)2是開始時間神僵、參數(shù)3是允許系統(tǒng)延遲的時間
am.setWindow(AlarmManager.RTC, System.currentTimeMillis() + timeMs, 500, sender);
} else {
am.setRepeating(AlarmManager.RTC, System.currentTimeMillis() + timeMs, 500, sender);
}
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 取消對應(yīng)action的定時器
* @param action action
*/
private void canalAlarm(String action,Context context) {
try {
Intent intent = new Intent(action);
PendingIntent pi = PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent,PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
if (am!=null)
am.cancel(pi);
} catch (Exception e){
e.printStackTrace();
}
}
2)添加喚醒鎖
- 在Android中還有一種喚醒鎖的機制,詳情可以百度學(xué)習(xí)一下覆劈。其實大概就是保礼,你可以使用喚醒鎖
強制讓系統(tǒng)不進行休眠!
/**
* Thanks: http://blog.csdn.net/wzj0808/article/details/52608940
* 保持系統(tǒng)CUP Active
*/
private static PowerManager.WakeLock wl;
public static void setCPUAliveLock(Context context) {
try {
if (wl!=null)
releaseKeepAliveOnScreenOff();
PowerManager pm = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE);
if (pm!=null) {
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScreenOff");
wl.acquire();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void releaseCPUAliveLock() {
try {
if (wl!=null)
wl.release();
wl = null;
} catch (Exception e) {
e.printStackTrace();
}
}
喚醒后责语,CPU處于ACTIVE狀態(tài)炮障,然后執(zhí)行任務(wù),執(zhí)行完成后坤候,便釋放鎖胁赢。
PS,如果在鎖屏的時候白筹,一直喚醒CPU智末,在實際應(yīng)用中,會出現(xiàn)一個非常嚴重的問題:耗電徒河!
所以系馆,推介是,使用定時器顽照,在定時器定觸發(fā)的時候由蘑,喚醒CPU,進行心跳代兵,心跳邏輯跑完后尼酿,就釋放鎖。