Android開發之如何監聽讓服務不被殺死(service+broadcast)(轉)


轉自:http://blog.csdn.net/primer_programer/article/details/25987439

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

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

我猜想它應該是相互監聽,如果有一方被kill掉,另一個捕獲到立即啟動,以達到service永遠都在運行的狀態,貌似360也是這個原理,具體是不是這個樣子,還有待參考,目前我還沒有參透它們是如何實現的,先簡單說一下我自己的防控措施吧。

 

 

一、Service簡介

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

 

本地服務 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()啟動service,系統將通過傳入的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。
如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那么將會增加該進程的重要性。

 

二、保證service不死

onStartCommand方法,返回START_STICKY。

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。

@Override  
public int onStartCommand(Intent intent, int flags, int startId) {  
    flags = START_STICKY;  
    return super.onStartCommand(intent, flags, startId);  
}  

【結論】 手動返回START_STICKY,親測當service因內存不足被kill,當內存又有的時候,service又被重新創建,比較不錯。


提升service進程優先級

在AndroidManifest.xml文件中對於intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時實用於廣播

<service  
    android:name="com.dbjtech.acbxt.waiqin.UploadService"  
    android:enabled="true" >  
    <intent-filter android:priority="1000" >  
        <action android:name="com.dbjtech.myservice" />  
    </intent-filter>  
</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 API將service放到前台狀態。這樣在低內存時被kill的幾率更低,但是如果在極度極度低內存的壓力下,該service還是會被kill掉。

在onStartCommand方法內

 Notification notification = new Notification(R.drawable.ic_launcher,  
 getString(R.string.app_name), System.currentTimeMillis());  
  
 PendingIntent pendingintent = PendingIntent.getActivity(this, 0,  
 new Intent(this, AppMain.class), 0);  
 notification.setLatestEventInfo(this, "uploadservice", "請保持程序在后台運行",  
 pendingintent);  
 startForeground(0x111, notification);

注意在onDestroy里還需要stopForeground(true);

 

 

在onDestroy方法里重啟service

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

<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
    <intent-filter>  
        <action android:name="android.intent.action.BOOT_COMPLETED" />  
        <action android:name="android.intent.action.USER_PRESENT" />  
        <action android:name="com.dbjtech.waiqin.destroy" />//這個就是自定義的action  
    </intent-filter>  
</receiver>  

在onDestroy時:

@Override  
public void onDestroy() {  
    stopForeground(true);  
    Intent intent = new Intent("com.dbjtech.waiqin.destroy");  
    sendBroadcast(intent);  
    super.onDestroy();  
}  

在BootReceiver里

public class BootReceiver extends BroadcastReceiver {  
  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {  
            //TODO  
            //在這里寫重新啟動service的相關操作  
                startUploadService(context);  
        }  
  
    }  
  
}  

【結論】當然,你也可以在onDestroy里直接startService(),我通過使用broadcast,可以做更多的處理判斷操作。

 


一個不被殺死的進程

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

<application  
    android:name="com.test.Application"  
    android:allowBackup="true"  
    android:icon="@drawable/ic_launcher"  
    android:label="@string/app_name"  
   <span style="color:#ff0000;"> android:persistent="true"</span>  
    android:theme="@style/AppTheme" >  
</application>  

【結論】據說這個屬性不能亂設置,不過設置后,的確發現優先級提高不少,或許是相當於系統級的進程。

 

 

系統各種廣播監聽

通過系統的廣播,監聽並捕獲到,然后判斷是否需要重新啟動service。

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >  
</uses-permission>  
<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
    <intent-filter>  
<span style="color:#ff0000;">        <action android:name="android.intent.action.BOOT_COMPLETED" />  
        <action android:name="android.intent.action.USER_PRESENT" /></span>  
        <action android:name="com.dbjtech.waiqin.destroy" />  
    </intent-filter>  
</receiver>  
@Override  
public void onReceive(Context context, Intent intent) {  
    if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
        System.out.println("手機開機了....");  
        startUploadService(context);  
    }  
    if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
            startUploadService(context);  
    }  
}  

三、結論

目前能想到的只有這么多,的確會讓自己的service提高不少的存活率,不過還不是完美的狀態,至於像微信或360手機衛士那樣,需要更多的操作,比如真正開啟了兩個APP,互相監聽,目前還不知道如何實現,以后再來補充。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM