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

Service簡介

一、Service java

每一個Service必須在manifest中 經過<service>來聲明。android

能夠經過contect.startservice和contect.bindserverice來啓動。shell

和其餘的應用組件同樣,運行在進程的主線程中。這就是說若是service須要不少耗時或者阻塞的操做,須要在其子線程中實現(或者用系統提供的IntentService,它繼承了Service,它處理數據是用自身新開的線程)。【固然你也能夠在新的線程中startService,這樣Service就不是在MainThread了緩存


二、本地服務 Local Service 用於應用程序內部微信

它能夠啓動並運行,直至有人中止了它或它本身中止。在這種方式下,它以調用Context.startService()啓動,而以調用Context.stopService()結束。它能夠調用Service.stopSelf() 或 Service.stopSelfResult()來本身中止。不論調用了多少次startService()方法,你只須要調用一次stopService()來中止服務。app

【用於實現應用程序本身的一些耗時任務,好比查詢升級信息,並不佔用應用程序好比Activity所屬線程,而是單開線程後臺執行,這樣用戶體驗比較好】eclipse


三、遠程服務 Remote Service 用於android系統內部的應用程序之間ide

它能夠經過本身定義並暴露出來的接口進行程序操做。客戶端創建一個到服務對象的鏈接,並經過那個鏈接來調用服務。鏈接以調用Context.bindService()方法創建,以調Context.unbindService()關閉。函數

多個客戶端能夠綁定至同一個服務。若是服務此時尚未加載,bindService()會先加載它。測試

【可被其餘應用程序複用,好比天氣預報服務,其餘應用程序不須要再寫這樣的服務,調用已有的便可】


四、Service的生命週期



五、Service運行方式

以startService()啓動服務,系統將經過傳入的Intent在底層搜索相關符合Intent裏面信息的service。若是服務沒有啓動則先運行onCreate,而後運行onStartCommand (可在裏面處理啓動時傳過來的Intent和其餘參數),直到明顯調用stopService或者stopSelf纔將中止Service。不管運行startService多少次,只要調用一次stopService或者stopSelf,Service都會中止。使用stopSelf(int)方 法能夠保證在處理好intent後再中止。onStartCommand在2.0後被引入用於service的啓動函數

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

五、擁有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幾個常量參數簡介:

一、START_STICKY

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

二、START_NOT_STICKY

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

三、START_REDELIVER_INTENT

在運行onStartCommand後service進程被kill後,系統將會再次啓動service,並傳入最後一個intent給onstartCommand。直到調用stopSelf(int)才中止傳遞intent。若是在被kill後還有未處理好的intent,

那被kill後服務仍是會自動啓動。所以onstartCommand不會接收到任何null的intent。

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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優先級


在CODE上查看代碼片派生到個人代碼片

  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 在CODE上查看代碼片 派生到個人代碼片
  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 shell
3,切換root模式,輸入:su     (若是root過就不會有錯誤)
4,設置System爲讀寫權限: mount –o remount rw /system (System默認爲只讀,沒法寫入,這一步很關鍵)

5,cd到sd卡跟目錄下,確認是否有咱們拷貝到sd卡根目錄下的apk(通常都是 storage/sdcard0)
shell@android:/ # cd storage/sdcard0
6,最關鍵的一步,咱們要把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等同樣的效果了。
 
轉自:http://blog.csdn.net/mad1989/article/details/22492519
相關文章
相關標籤/搜索