這幾天一直在准備考試,總算有個半天時間可以休息下,寫寫博客。
如何讓Service keep alive是一個很常見的問題。
在APP開發過程中,需要Service持續提供服務的應用場景太多了,比如鬧鐘需要作出及時提醒,那麼比如得有一個Service不斷去比較當前時間和設置時間;QQ要能流暢的聊天,必然也需要及時接收消息等。
但是Android並沒有保證Service有這樣功能,畢竟一個系統面對的是用戶,必然以對用戶友好為先。
關於如何讓Service keep alive,我在上篇博客給出的解決方案是:方案一,讓服務器端發一個推送,檢查Service是否還存活;方案二,將Service獨立出來,運行在另一個進程中。
這兩個方案有些地方需要說明和改進,然後還會有其他方案補充進來。
方案一:利用推送來確保Service存活。
方案一的做法有點“偷懶”。因為相當於把這個難題轉移給推送服務提供者來處理,或者說,只需要依靠推送,就不需要自己去考慮存活問題。
推送一直是移動客戶端開發的熱門話題(實際上也是傳統軟件開發的熱門話題)。
一般情況下,C/S結構(B/S是特殊的C/S結構)中的業務流程是這樣的:客戶端主動向服務器端發出請求,服務器端響應請求,建立二者之間的連接。通過建立的鏈接,雙方可以發送/接收數據。最後客戶端和服務器端斷開連接。也就是說,客戶端是主動方,是請求連接的發起者;服務器端保持對某個端口的監聽(0-1023是系統端口號,比如80端口被指派給HTTP),而被動地等待客戶端的連接請求。
那麼推送指的是,服務器端在沒有收到請求的情況下主動向客戶端發送信息,比如服務器端收到一封郵件後主動發往郵件的客戶端。
推送是如何實現的呢?首先,服務器端的功能還是不變,監聽某個端口等待請求。然後我們可以看到,服務器端能主動發送信息的唯一時間段就是在建立了服務器端到客戶端的連接之後、二者斷開連接之前。因此,推送的實現就是基於保持服務器端和客戶端的連接一直存在,方式可以是持續連接或者輪詢。
回到Service這個問題上來,推送服務的提供者必然會保證推送技術的穩定性,依靠推送,可以喚醒我們的APP,那麼保證Service的存活也就不在話下。
方案二:將Service運行在另一個進程中。
將Service放在另一個進程中,可以避免APP所在的進程因資源緊張或者被用戶手動結束的時候,Service也被結束。
這個做法的另一個優點是,可以讓Service獨立出來,為同一個公司的不同APP提供相同的服務。比如當大部分APP都需要同一個信息的時候,為每個APP都寫一個Service顯然不好,這時完全可以寫一個獨立進程中的Service,為所有APP提供信息。
當然,這個方案有個缺點是,當某些手機助手無腦式結束掉全部的非系統進程時,Service無法存活。
方案三:讓onStartCommand()函數的返回值為START_STICKY,同時在onDestroy()中重啟Service
當返回值為該值時,Service被kill之後會被系統自動重啟。
同時,在Service的onDestroy()中重啟Service,可以給Service的重啟做雙重保證。
但是顯然的缺點是,當APP的進程被kill後,這個方案就會失效。
方案四:使用“守護Service”。
即除了你需要存活的Service外,專門寫一個Service,並使該Service運行在另一個進程中。
當兩個Service中有一個Service被kill,就在另一個Service中去重啟該Service。
從手機中進程的數量上判斷,搜狐視頻、觸寶號碼助手等使用的正是該方式。
方案五:將Service所在的APP提升至系統應用級別
在配置文件中的Application節點做這樣的設置:android:persistent=”true”可以將APP提升至系統級別。
相信不管什麼手機助手都不會去kill這一塊的APP。
從手機中QQ被手動kill後系統出現的對話框判斷,手機QQ正是使用的這一方案。
方案六:接收系統的廣播
可以用BroadCastReceiver去接收系統的廣播,比如時間變化的廣播、電量變化的廣播等。
這樣基本上Service就無法被kill了。
總結:
能保證Service完全不會被殺死的方案是方案一和方案六。
比較輕量的方案是方案二和方案三。
我個人認為比較好的方案是方案四和方案五——那些大廠選擇這樣的方案是有道理的。具體使用哪個得看自己的需求。
雖然標題是說保持Service持續存活,但是並不是說一定要在任何情況下存活。十分不建議使用方案一和方案六。因為當用戶發現,不管他怎麼操作都無法停止一個APP時,帶給他的只有恐慌,那麼小手一抖的情況下,APP就只能被刪除了。