Android幾種Service常駐記憶體的小思路
老話說的好:躲得了初一,躲不過高三 ! 大多數的Android開發者遇到的一個問題—如何保證Service常駐記憶體! 最近我終於也在專案中務必幸運的遇到了
先來了解一下什麼是Service常駐記憶體。
所謂Service常駐記憶體,意思就是想讓自己寫的Service服務在手機開機之後就永遠處於執行狀態。 舉個Example先, 例如大家最熟悉的微信和QQ,每當手機開機之後,微信和QQ都是自動就在後臺執行,實時的接收聊天資訊,並且QQ和微信幾乎是永遠處於執行狀態(即使是使用者通過各種暴力方式將Service服務關掉頁也沒用)
再看一下Service可能被關掉的幾種方式
第一種 Android系統有一個記憶體保護機制,當系統可用記憶體不足時,為了保證當前正在執行的程序(或者說Activity)能夠正常執行,系統會主動的將一些優先順序比較低的程序給殺掉,當然在此程序中的Service也會隨著一起被殺死。
對於程序的優先順序是怎麼排的 可以參考一下幾個優先順序排序的規則 :
在Android中一共有5種程序的分類,按照優先順序從高到低的順序分別是:
前臺程序
- a . 程序中包含處於前臺的正與使用者互動的activity;
- b. 程序中包含與前臺activity繫結的service;
- c. 程序中包含呼叫了startForeground()方法的service;
- d. 程序中包含正在執行onCreate(), onStart(), 或onDestroy()方法的service;
- e. 程序中包含正在執行onReceive()方法的BroadcastReceiver.
- f. 系統中前臺程序的數量很少, 前臺程序幾乎不會被殺死. 只有當記憶體低到無法保證所有的前臺程序同時執行時才會選擇殺死某個前臺程序.
可視程序
- a. 程序中包含未處於前臺但仍然可見的activity(呼叫了activity的onPause()方法, 但沒有呼叫onStop()方法). 典型的情況是執行activity時彈出對話方塊, 此時的activity雖然不是前臺activity, 但其仍然可見.
- b. 程序中包含與可見activity繫結的service.
- c. 可視程序不會被系統殺死, 除非為了保證前臺程序的執行而不得已為之
服務程序
- 程序中包含已啟動的service.
後臺程序
- a. 程序中包含不可見的activity(onStop()方法呼叫後的activity).
- b. 後臺程序不會直接影響使用者體驗, 為了保證前臺程序/可視程序/服務程序的執行, 系統隨時都有可能殺死一個後臺程序.
- c. 一個正確的實現了生命週期方法的activity處於後臺時被系統殺死, 可以在使用者重新啟動它時恢復之前的執行狀態.
空程序
- 不包含任何處於活動狀態的程序是一個空程序. 系統經常殺死空程序, 這不會造成任何影響. 空程序存在的唯一理由是為了快取一些啟動資料, 以便下次可以更快的啟動.
第二種 使用者可以在App管理介面主動的將Service服務強制關閉,如圖所示:
第三種 就是現在市面上常見的一些Clear Master之類的App清理器,例如MIUI自帶的清空RAM、手機管家、360等等。 通過這些類似的三方APP,也可以將正在執行中的程序給強制關閉
最後再來看一下我們這篇文章的主題:如何建立一個在以上三種清空下都不會被強制關閉的Service(聽起來有點吊炸天的趕腳)
首先我們需要了解一點,以上三種方式其實在最終framework層都是呼叫PM的killProcess方法將某程序給直接殺死,但是我們在做上層App開發時(原始碼下的二次開發另當別論),又無法修改framework層的相關程式碼,所以我們不可能從根本上避開程序被殺死這一行為。
因此我的思路就是如果提供某一種機制,這種機制可以在規定時間內,頻繁的去啟動某Service, 而如果我們去啟動一個已經被建立的Service時,它的onCreate方法是不會再被呼叫的。
具體實現方式有如下幾種:
1 修改Service的onStartCommand方法中的返回值。onStartCommand方法有三種返回值,依次是
START_NOT_STICK:
- 當Service被異常殺死時,系統不會再去嘗試再次啟動這個Service
START_STICKY
- 當Service被異常殺死時,系統會再去嘗試再次啟動這個Service,但是之前的Intent會丟失,也就是在onStartCommand中接收到的Intent會是null
START_REDELIVER_INTENT
- 當Service被異常殺死時,系統會再去嘗試再次啟動這個Service,並且之前的Intent也會重新被傳給onStartCommand方法
通過修改onStartCommand方法的返回值這一方法足以解決上面我們提到Service被關閉的第一種情況。 但是對於使用者主動強制關閉和三方管理器還是沒有效果的
2 通過監聽某些系統經常發出的廣播,當接收到廣播之後我們可以主動的去嘗試啟動Service,如果此Service已經被建立,則不會再走onCreate方法,否則這個Service就會被再次啟動.
舉幾個常用的系統廣播的例子:
- Intent.ACTION_BATTERY_CH 電池電量發生改變
- Intent.ACTION_AIRPLANE_MODE_CHANGED; 開啟或關閉飛航模式
- Intent.PHONE_STATE_CHANGED_ACTION 電話狀態發生改變
這種方法可以解決Service被關閉的所有情景。 但是缺點是不是很穩定,畢竟要接收到某些系統廣播之後才能執行啟動Service的操作,因此有一定的延時,甚至沒有成功的再次啟動Service (對於希望Service被再次啟動的渴望非常強烈的童鞋,不建議使用這種方法)
3 通過呼叫AlarmManager的setRepeating方法,我們可以每隔一段時間就去啟動Service一次,程式碼如下所示:
Calendar cal = Calendar.getInstance();
Intent intent = new Intent(this, MyService.class);
PendingIntent pintent = PendingIntent.getService(this, 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// 每分鐘啟動一次,這個時間值視具體情況而定
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 60*1000, pintent);
這種方式也可以解決Service被關閉的所有情景。並且可以在一分鐘內不斷的去嘗試啟動Service,這個時間值是可以自己調的。