Android 開發之Service 探索如何保證Service不被殺死或被kill之后自動重啟

xkfm5730 8年前發布 | 22K 次閱讀 安卓開發 Android開發 移動開發

前言:

在我司項目1.0版本的時候消息是使用的環信、用了之后發現各種bug,各種機型不支持導致app崩潰,于是在2.0版本果斷去掉環信,使用了公眾號用的那套消息系統(老大自己寫的)并做了擴展升級。搞了近半個月終于是搞完了,項目也順利上線......

當時檢測未讀消息/新消息我寫了個線程,每隔30s去請求一個,好low的,app退出后你就拜拜了吧,肯定要改啊!用什么?service唄,于是開始service之旅...

廢話連篇,開始我們的Service之旅吧!

1.我們要知道什么是Service?

A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a correspondingdeclaration in its package'sAndroidManifest.xml. Services can be started with Context.startService() and Context.bindService() .

呵呵,你看得懂?

廢話...

簡單解釋下就是: Service 是一個應用程序組件,它能夠在后臺執行一些耗時較長的操作,并且不提供用戶界面。服務能被其它應用程序的組件啟動,即使用戶切換到另外的應用時還能保持后臺運行。此外,應用程序組件還能與服務綁定,并與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網絡傳輸、音樂播放、執行文件I/O、或者與content provider進行交互,所有這些都是后臺進行的。

2.Service生命周期

一會我們通過代碼看結果,先了解...

3.Service基本類型(啟動方式):

Started :通過應用程序組件(例如Activity)調用 startService() 啟動服務:StartService(intent)系統通通過傳入的intent搜索相關符合intent的Service,

依次執行其相關生命周期,service一旦啟動就會一直在后臺運行,直到調用stopService或stopSelf停止服務。

注:public void onStart(Intent intent, int startId) {}已過時,在2.0之后引入public int onStartCommand(Intent intent, int flags, int startId) {},flags,Service啟動函數,后面介紹。

Bind:通過bindService()綁定服務,該提供了一個客戶端/服務器接口,允許組建與服務進行交互、發送請求、返回結果,設置可以利用進程間通信夸進程執行這些操作;多個組件

可以同時與一個服務綁定,通過onUnbind()方法解綁服務,當所有組件解綁后,服務也被銷毀。

接下來正式進入我們今天的話題:如何保證Service不被殺死或被kill之后自動重啟!

1).onStartCommand()

返回常量Flag介紹:

START_STICKY 表示你希望系統可用的時候自動重啟你的服務,但你不關心是否能獲得最后一次的 Intent (例如,你可以重建自己的狀態或者控制自己的 start/stop 生命周期)。

START_REDELIVER_INTENT 是為那些在被殺死之后重啟時重新獲得 Intent 的服務的,直到你用傳遞給 onStartCommand() 方法的 startId 參數調用 stopSelf() 為止。這里你會使用 Intent 和 startId 作為隊列完成工作。

START_NOT_STICKY 用于那些殺掉也沒關系的服務。這適合那些管理周期性任務的服務,它們只是等待下一個時間窗口工作。(摘自掘金

so,在內存不足服務被kill時,我們手動返回flag為START_STICKY / START_REDELIVER_INTENT(取決于重啟是否需要重新獲得intent),當內存足夠時,服務會被重新創建.此方法然并卵,并不能使服務常駐...

@Overridepublic intonStartCommand(Intent intent, intflags, intstartId) {    Log.i("TAG","Services onStartCommand");return START_REDELIVER_INTENT ;}

2).配置android:persistent="true"  ,persistent根據字面意思理解是持久...持久要持久!也就是常駐。但是通過測試發現被kill掉之后并不能重啟...

3).查看其官方文檔,有 startForeground 這個方法

startForeground (int id, Notification notification)

Make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state.

其意思就是使服務在前臺運行,發送一個通知給處于此狀態的用戶,前臺必須提供一個狀態欄通知

這里就涉及到service的進程優先級:當系統內存不足需要釋放時,會按照優先級對進程回收,而android將進程分為六個等級

前臺進程(

FOREGROUND_APP)、可視進程(VISIBLE_APP )、次要服務進程(SECONDARY_SERVER )

后臺進程

(HIDDEN_APP)、內容供應節點(CONTENT_PROVIDER)、空進程(EMPTY_APP)

可在service onStartCommand()方法中如此操作:

NotificationCompat.Builder builder =newNotificationCompat.Builder(G. applicationContext );Notification notification = builder.build();notification.flags= Notification. FLAG_FOREGROUND_SERVICE ;startForeground(0,notification);Log.i("Service","UnreadMessageServices onStartCommand");return START_STICKY ;

寫一個狀態通知欄大家都會吧,這里就不詳說了,不會的自行google...

運行后效果為 如圖:

我們將service置為前臺服務時,一般情況還是不想讓用戶看到對吧。 startForeground() 方法。此方法有兩個參數:唯一標識通知的整數值、狀態欄通知 Notification 對象。當我傳id不為0時, Notification 會顯示,當id=0時則不顯示到通知欄。

這樣做只是在低內存是降低該service被kill掉的幾率,并不能真正使service常駐。

4).還有方法是說讓其成為系統應用...  好吧這個我確實沒測試,也不想..聽說apk無法卸載;

設置該service為獨立進程,貌似提升了優先級,但是照樣被kill...  pass ;

在onDestory()方法中重啟service,能不能不用這么low的方法...我沒做測試

這幾種方法只能提升service優先級/存活率,但是還不能解決其被殺毒軟件強行kill的命運...

我的解決方案:

1.首先設置服務為開機自啟

public classBootBroadcastReceiverextendsBroadcastReceiver {

@Override

public voidonReceive(Context context,Intent intent) {

Intent unreadCountService =newIntent(context,UnreadCountService.class);

context.startService(unreadCountService);

Log.d("MessageService","開機服務自啟...");

}}

同時需要配置其權限AndroidManifest.xml

2.利用系統廣播Intent.ACTION_TIME_TICK 每隔一分鐘檢測一次Service的運行狀態

public classCheckBroadCastReceiverextendsBroadcastReceiver {

@Override

public voidonReceive(Context context,Intent intent) {

if(intent.getAction().equals(Intent.ACTION_TIME_TICK)) {

//檢查Service狀態

Log.e("MessageService","onReceive: "+"檢測MessageService是否運行 :"+

UtilsHelper.isServiceRunning(context,"com.dailylifeapp.app.and.dailylife.helper.UnreadCountService"));

if(UtilsHelper.isServiceRunning(context,"com.dailylifeapp.app.and.dailylife.helper.UnreadCountService") ==false) {

//重啟服務

Intent i =newIntent(context,UnreadCountService.class);

context.startService(i);

}}}}

公司項目該功能做到這一步就已經足夠了,暫時也不打算深入研究了。作為Android開發者,我們本身是有必要去維護Android的生態環境而不是一昧的去破壞... 到此為止!菜鳥寫博客,諸多不合之處歡迎指出,還望無噴...

 

來自:http://www.jianshu.com/p/e7be29d6cd46

 

 本文由用戶 xkfm5730 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!