老話說的好:躲得了初一,躲只是高三 ! 大多數的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。這個時間值是能夠自己調的。