[轉] Android開發之如何保證Service不被殺掉(broadcast+system/app)

來源:互聯網
上載者:User

標籤:round   lib   優先順序   列表   java   int   運行   方法   資源   

轉寄:原文連結http://blog.csdn.net/mad1989/article/details/22492519

序言

 

最近項目要實現這樣一個效果:運行後,要有一個service始終保持在後台運行,不管使用者作出什麼操作,都要保證service不被kill,這可真是一個難題。參考了現今各種定製版的系統和安全廠商牛虻軟體,如何能保證自己的Service不被殺死呢?

其實除了常規的手段,我們可以參考一下和360,設定-程式-正在運行,可以看到是同時開啟了兩個進程和服務:

【有興趣可以研究一下 守護進程 和 AIDL 】

我猜想它應該是相互監聽,如果有一方被kill掉,另一個捕獲到立即啟動,以達到service永遠都在啟動並執行狀態,貌似360也是這個原理,具體是不是這個樣子,還有待參考,目前我還沒有參透它們是如何?的,先簡單說一下我自己的防控措施吧,首先介紹一下Service概念,記性不好,重複造一下車輪,高手可以直接看最後。

 

 

Service簡介

Service是在一段不定的時間運行在後台,不和使用者互動應用組件。每個Service必須在manifest中 通過<service>來聲明。可以通過contect.startservice和contect.bindserverice來啟動。和其他的應用組件一樣,運行在進程的主線程中。這就是說如果service需要很多耗時或者阻塞的操作,需要在其子線程中實現(或者用系統提供的IntentService,它繼承了Service,它處理資料是用自身新開的線程)。【當然你也可以在新的線程中startService,這樣Service就不是在MainThread了

 

本地服務 Local Service 用於應用程式內部

它可以啟動並運行,直至有人停止了它或它自己停止。在這種方式下,它以調用Context.startService()啟動,而以調用Context.stopService()結束。它可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調用了多少次startService()方法,你只需要調用一次stopService()來停止服務。

【用於實現應用程式自己的一些耗時任務,比如查詢升級資訊,並不佔用應用程式比如Activity所屬線程,而是單開線程後台執行,這樣使用者體驗比較好】

 

遠程服務 Remote Service 用於Android系統內部的應用程式之間

它可以通過自己定義並暴露出來的介面進行程式操作。用戶端建立一個到服務物件的串連,並通過那個串連來調用服務。串連以調用Context.bindService()方法建立,以調用 Context.unbindService()關閉。多個用戶端可以綁定至同一個服務。如果服務此時還沒有載入,bindService()會先載入它。

【可被其他應用程式複用,比如天氣預報服務,其他應用程式不需要再寫這樣的服務,調用已有的即可】

 

1,Service的生命週期

 

2,Service運行方式

以startService()啟動服務,系統將通過傳入的Intent在底層搜尋相關符合Intent裡面資訊的service。如果服務沒有啟動則先運行onCreate,然後運行onStartCommand (可在裡面處理啟動時傳過來的Intent和其他參數),直到明顯調用stopService或者stopSelf才將停止Service。無論運行startService多少次,只要調用一次stopService或者stopSelf,Service都會停止。使用stopSelf(int)方法可以保證在處理好intent後再停止。onStartCommand ,在2.0後被引入用於service的啟動函數,2.0之前為public void onStart(Intent intent, int startId) 。

以bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止。onBind()只有採用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。採用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。

 

3,擁有service的進程具有較高的優先順序

官方文檔告訴我們,android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者用戶端串連(bindService)到它。當記憶體不足時,需要保持,擁有service的進程具有較高的優先順序。

1. 如果service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變為前台進程以避免被killed。
2. 如果當前service已經被啟動(start),擁有它的進程則比那些使用者可見的進程優先順序低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.
3. 如果用戶端已經串連到service (bindService),那麼擁有Service的進程則擁有最高的優先順序,可以認為service是可見的。
4. 如果service可以使用startForeground(int, Notification)方法來將service設定為前台狀態,那麼系統就認為是對使用者可見的,並不會在記憶體不足時killed。
5. 如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。

 

保證service不被殺掉

 

onStartCommand方法,返回START_STICKY

 

StartCommond幾個常量參數簡介:

1、START_STICKY

在運行onStartCommand後service進程被kill後,那將保留在開始狀態,但是不保留那些傳入的intent。不久後service就會再次嘗試重新建立,因為保留在開始狀態,在建立     service後將保證調用onstartCommand。如果沒有傳遞任何開始命令給service,那將擷取到null的intent。

2、START_NOT_STICKY

在運行onStartCommand後service進程被kill後,並且沒有新的intent傳遞給它。Service將移出開始狀態,並且直到新的明顯的方法(startService)調用才重新建立。因為如果沒有傳遞任何未決定的intent那麼service是不會啟動,也就是期間onstartCommand不會接收到任何null的intent。

3、START_REDELIVER_INTENT

在運行onStartCommand後service進程被kill後,系統將會再次啟動service,並傳入最後一個intent給onstartCommand。直到調用stopSelf(int)才停止傳遞intent。如果在被kill後還有未處理好的intent,那被kill後服務還是會自動啟動。因此onstartCommand不會接收到任何null的intent。

 

[java] view plain copy 
  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     flags = START_STICKY;  
  4.     return super.onStartCommand(intent, flags, startId);  
  5. }  

 

【結論】 手動返回START_STICKY,親測當service因記憶體不足被kill,當記憶體又有的時候,service又被重新建立,比較不錯,但是不能保證任何情況下都被重建,比如進程被幹掉了....

 

 

提升service優先順序

 

在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數字越小則優先順序越低,同時適用於廣播。

 

[java] view plain copy 
  1. <service  
  2.     android:name="com.dbjtech.acbxt.waiqin.UploadService"  
  3.     android:enabled="true" >  
  4.     <intent-filter android:priority="1000" >  
  5.         <action android:name="com.dbjtech.myservice" />  
  6.     </intent-filter>  
  7. </service>  

 

【結論】目前看來,priority這個屬性貌似只適用於broadcast,對於Service來說可能無效

 

提升service進程優先順序

 

Android中的進程是託管的,當系統進程空間緊張的時候,會依照優先順序自動進行進程的回收。Android將進程分為6個等級,它們按優先順序順序由高到低依次是:

   1.前台進程( FOREGROUND_APP)
   2.可視進程(VISIBLE_APP )
   3. 次要服務進程(SECONDARY_SERVER )
   4.後台進程 (HIDDEN_APP)
   5.內容供應節點(CONTENT_PROVIDER)
   6.空進程(EMPTY_APP)

當service運行在低記憶體的環境時,將會kill掉一些存在的進程。因此進程的優先順序將會很重要,可以使用startForeground 將service放到前台狀態。這樣在低記憶體時被kill的幾率會低一些。

 

在onStartCommand方法內添加如下代碼:

 

[java] view plain copy 
  1.  Notification notification = new Notification(R.drawable.ic_launcher,  
  2.  getString(R.string.app_name), System.currentTimeMillis());  
  3.   
  4.  PendingIntent pendingintent = PendingIntent.getActivity(this, 0,  
  5.  new Intent(this, AppMain.class), 0);  
  6.  notification.setLatestEventInfo(this, "uploadservice", "請保持程式在後台運行",  
  7.  pendingintent);  
  8. <span style="color:#ff0000;"> startForeground(0x111, notification);</span>  

注意在onDestroy裡還需要stopForeground(true),運行時在下拉式清單會看到自己的APP在:

 

【結論】如果在極度極度低記憶體的壓力下,該service還是會被kill掉,並且不一定會restart

 

 

onDestroy方法裡重啟service

 

service +broadcast  方式,就是當service走ondestory的時候,發送一個自訂的廣播,當收到廣播的時候,重新啟動service;

 

[java] view plain copy 
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="com.dbjtech.waiqin.destroy" />//這個就是自訂的action  
  6.     </intent-filter>  
  7. </receiver>  

在onDestroy時:

 

[java] view plain copy 
  1. @Override  
  2. public void onDestroy() {  
  3.     stopForeground(true);  
  4.     Intent intent = new Intent("com.dbjtech.waiqin.destroy");  
  5.     sendBroadcast(intent);  
  6.     super.onDestroy();  
  7. }  

在BootReceiver裡

 

[java] view plain copy 
  1. public class BootReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {  
  6.             //TODO  
  7.             //在這裡寫重新啟動service的相關操作  
  8.                 startUploadService(context);  
  9.         }  
  10.   
  11.     }  
  12.   
  13. }  

也可以直接在onDestroy()裡startService

 

[java] view plain copy 
  1. @Override  
  2. public void onDestroy() {  
  3.   
  4.      Intent sevice = new Intent(this, MainService.class);  
  5.      this.startService(sevice);  
  6.   
  7.     super.onDestroy();  
  8. }  

 

 

【結論】當使用類似口口管家等第三方應用或是在setting裡-應用-強制停止時,APP進程可能就直接被幹掉了,onDestroy方法都進不來,所以還是無法保證~.~

 


Application加上Persistent屬性

 

看Android的文檔知道,當進程長期不活動,或系統需要資源時,會自動清理門戶,殺死一些Service,和不可見的Activity等所在的進程。但是如果某個進程不想被殺死(如資料緩衝進程,或狀態監控進程,或遠程服務進程),可以這麼做:

 

[java] view plain copy 
  1. <application  
  2.     android:name="com.test.Application"  
  3.     android:allowBackup="true"  
  4.     android:icon="@drawable/ic_launcher"  
  5.     android:label="@string/app_name"  
  6.    <span style="color:#ff0000;"> android:persistent="true"</span>  
  7.     android:theme="@style/AppTheme" >  
  8. </application>  

 

【結論】據說這個屬性不能亂設定,不過設定後,的確發現優先順序提高不少,或許是相當於系統級的進程,但是還是無法保證存活

 

 

監聽系統廣播判斷Service狀態

 

通過系統的一些廣播,比如:手機重啟、介面喚醒、應用狀態改變等等監聽並捕獲到,然後判斷我們的Service是否還存活,別忘記加許可權啊。

 

[java] view plain copy 
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="android.intent.action.PACKAGE_RESTARTED" />  
  6.         <action android:name="com.dbjtech.waiqin.destroy" />  
  7.     </intent-filter>  
  8. </receiver>  

BroadcastReceiver中:

 

[java] view plain copy 
  1. @Override  
  2. public void onReceive(Context context, Intent intent) {  
  3.     if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
  4.         System.out.println("手機開機了....");  
  5.         startUploadService(context);  
  6.     }  
  7.     if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
  8.             startUploadService(context);  
  9.     }  
  10. }  

 

【結論】這也能算是一種措施,不過感覺監聽多了會導致Service很混亂,帶來諸多不便

將APK安裝到/system/app,變身系統級應用 這個辦法不推薦使用,因為如果你的APP如果是給使用者使用的,那就不合適了,我是為了給測試的妹子來用,這個APP的目的也是很簡單,開啟後開啟Service並且能保證一直在後台駐留,開機自啟動。但是昨天發現如果她的HuaWei手機長時間關閉, 再重新開啟時,我們應用的Service不會自啟動,貌似廣播收不到了~一怒之下,打算搞成系統應用。 前提:ROOT過的手機1,把代碼編寫好後,打包匯出apk,copy到手機SD卡根目錄下。2,手機串連eclipse,cmd: adb shell3,切換root模式,輸入:su     (如果root過就不會有錯誤)4,設定System為讀寫權限:mount –o remount rw /system (System預設為唯讀,無法寫入,這一步很關鍵)5,cd到sd卡跟目錄下,確認是否有我們拷貝到sd卡根目錄下的apk(一般都是 storage/sdcard0)[email protected]:/ # cd storage/sdcard06,最關鍵的一步,我們要把apk拷貝到 /System/app中:發現copy命令無效~那麼我們就用push把:如果有錯誤:device not found,那麼手機下載一個Root Explorer把,找到apk,copy到System/app下,通過這個APP要更容易一些。7,system/app 確定擁有我們的APK後,重啟手機把:設定-應用程式管理,查看一下:可以看到我們的APP已經無法卸載了,只能停用這個時候,就算強制停止,或是關閉Service,重啟手機後照樣可以起來Service~! 系統級的APP,這樣一些第三方的管家軟體,就無法殺掉我們,除非自己把APP停用掉,或是強制停止(但是我的APP可以開機自啟動)。 【結論】這種方式適合調試來用,並不算是一種解決辦法,大家可以嘗試在正在啟動並執行介面:強制關閉搜狐視頻的兩個進程,重啟手機,發現他又可以自啟動,但是如果換成我們的APP,強制停止,進程掛了,再重啟手機,無法自啟動了~   大家一起研究,怎麼樣才能像搜狐視頻一樣,開啟兩個進程,相互監聽,做到最大程度的存活,如果這個能實現了,那就和、360等一樣的效果了。

[轉] Android開發之如何保證Service不被殺掉(broadcast+system/app)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.