Android 保持Service不被Kill掉的方法--双Service守护 Android实现双进程守护
本文分為兩個部分,第一部分為雙Service守護,第二部分為雙進程守護
第一部分:
一、Service簡介:
Java.lang.Object
???Android.content.Context
???????android.content.ContextWrapper
???????????android.app.Service
Service是應(yīng)用程序Application的一個組件(component)。
它的作用有兩點:1.用來提供一個長期在后臺運行并且不與用戶交互的操作,2.也可以為其他應(yīng)用程序提供服務(wù)。
Service必須和其他四大組件一樣,使用<service>標簽在AndroidManifest.xml中進行聲明。
啟動service有兩種方式Context.startService() 和 Context.bindService()。
注意,除了特別指定外,service并不是單獨的進程,一般service在其宿主進程的主線程(UI Thread)中運行【當然也可以在新的線程中startService,這樣Service就不是在MainThread了】。這意味著,如果您的服務(wù)要做任何 耗時(如 MP3 播放) 或阻塞 (比如網(wǎng)絡(luò)) 操作,它應(yīng)該產(chǎn)生它自己的線程,用來做那項工作。(service不是單獨的進程也不是單獨的線程)
Service提供了兩大功能:
Context.startService()用來在后臺啟動一個服務(wù);
Context.bindService()用來綁定其他服務(wù),以此來獲取其他service提供的服務(wù);
?
本地服務(wù) Local Service?用于應(yīng)用程序內(nèi)部
它可以啟動并運行,直至有人停止了它或它自己停止。在這種方式下,它以調(diào)用Context.startService()啟動,而以調(diào)用Context.stopService()結(jié)束。它可以調(diào)用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調(diào)用了多少次startService()方法,你只需要調(diào)用一次stopService()來停止服務(wù)。
【用于實現(xiàn)應(yīng)用程序自己的一些耗時任務(wù),比如查詢升級信息,并不占用應(yīng)用程序比如Activity所屬線程,而是單開線程后臺執(zhí)行,這樣用戶體驗比較好】
?
遠程服務(wù) Remote Service?用于android系統(tǒng)內(nèi)部的應(yīng)用程序之間
它可以通過自己定義并暴露出來的接口進行程序操作。客戶端建立一個到服務(wù)對象的連接,并通過那個連接來調(diào)用服務(wù)。連接以調(diào)用Context.bindService()方法建立,以調(diào)用 Context.unbindService()關(guān)閉。多個客戶端可以綁定至同一個服務(wù)。如果服務(wù)此時還沒有加載,bindService()會先加載它。
【可被其他應(yīng)用程序復(fù)用,比如天氣預(yù)報服務(wù),其他應(yīng)用程序不需要再寫這樣的服務(wù),調(diào)用已有的即可】
?
?
二、Service運行方式和生命周期圖:
以startService()啟動服務(wù),系統(tǒng)將通過傳入的Intent在底層搜索相關(guān)符合Intent里面信息的service。如果服務(wù)沒有啟動則先運行onCreate,然后運行onStartCommand (可在里面處理啟動時傳過來的Intent和其他參數(shù)),直到明顯調(diào)用stopService或者stopSelf才將停止Service。無論運行startService多少次,只要調(diào)用一次stopService或者stopSelf,Service都會停止。使用stopSelf(int)方法可以保證在處理好intent后再停止。onStartCommand ,在2.0后被引入用于service的啟動函數(shù),2.0之前為public void onStart(Intent intent, int startId) 。
以bindService()方法啟用服務(wù),調(diào)用者與服務(wù)綁定在了一起,調(diào)用者一旦退出,服務(wù)也就終止。onBind()只有采用Context.bindService()方法啟動服務(wù)時才會回調(diào)該方法。該方法在調(diào)用者與服務(wù)綁定時被調(diào)用,當調(diào)用者與服務(wù)已經(jīng)綁定,多次調(diào)用Context.bindService()方法并不會導(dǎo)致該方法被多次調(diào)用。采用Context.bindService()方法啟動服務(wù)時只能調(diào)用onUnbind()方法解除調(diào)用者與服務(wù)解除,服務(wù)結(jié)束時會調(diào)用onDestroy()方法。
?
(注意這個新老API的改變)
void onStart(Intent intent, int startId)
This method was deprecated????? in API level 5.??? Implement onStartCommand(Intent, int, int) instead.
?
int onStartCommand(Intent intent, int flags, int startId)
Called by the system every time a client explicitly starts the service by calling? startService(Intent), providing the arguments it supplied and a? unique integer token representing the start request.
?
三、Service的優(yōu)先級
官方文檔告訴我們,Android系統(tǒng)會盡量保持擁有service的進程運行,只要在該service已經(jīng)被啟動(start)或者客戶端連接(bindService)到它。當內(nèi)存不足時,需要保持,擁有service的進程具有較高的優(yōu)先級。
1. 如果service正在調(diào)用onCreate,onStartCommand或者onDestory方法,那么用于當前service的進程則變?yōu)榍芭_進程以避免被killed。
2. 如果當前service已經(jīng)被啟動(start),擁有它的進程則比那些用戶可見的進程優(yōu)先級低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.
3. 如果客戶端已經(jīng)連接到service (bindService),那么擁有Service的進程則擁有最高的優(yōu)先級,可以認為service是可見的。
4. 如果service可以使用startForeground(int, Notification)方法來將service設(shè)置為前臺狀態(tài),那么系統(tǒng)就認為是對用戶可見的,并不會在內(nèi)存不足時killed。
5. 如果有其他的應(yīng)用組件作為Service,Activity等運行在相同的進程中,那么將會增加該進程的重要性。
?
四、保持service不被kill掉
方法一:
START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them
onStartCommand方法幾個返回值簡介:
?
1、START_STICKY
?
在運行onStartCommand后service進程被kill后,那將保留在開始狀態(tài),但是不保留那些傳入的intent。不久后service就會再次嘗試重新創(chuàng)建,因為保留在開始狀態(tài),在創(chuàng)建???? service后將保證調(diào)用onstartCommand。如果沒有傳遞任何開始命令給service,那將獲取到null的intent。
?
2、START_NOT_STICKY
?
在運行onStartCommand后service進程被kill后,并且沒有新的intent傳遞給它。Service將移出開始狀態(tài),并且直到新的明顯的方法(startService)調(diào)用才重新創(chuàng)建。因為如果沒有傳遞任何未決定的intent那么service是不會啟動,也就是期間onstartCommand不會接收到任何null的intent。
?
3、START_REDELIVER_INTENT
?
在運行onStartCommand后service進程被kill后,系統(tǒng)將會再次啟動service,并傳入最后一個intent給onstartCommand。直到調(diào)用stopSelf(int)才停止傳遞intent。如果在被kill后還有未處理好的intent,那被kill后服務(wù)還是會自動啟動。因此onstartCommand不會接收到任何null的intent。
【結(jié)論】 手動返回START_STICKY,親測當service因內(nèi)存不足被kill,當內(nèi)存又有的時候,service又被重新創(chuàng)建,比較不錯,但是不能保證任何情況下都被重建,比如進程被干掉了....
方法二:
提升service優(yōu)先級
?在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設(shè)置最高優(yōu)先級,1000是最高值,如果數(shù)字越小則優(yōu)先級越低,同時適用于廣播。
<serviceandroid:name="com.dbjtech.acbxt.waiqin.UploadService"android:enabled="true" ><intent-filter android:priority="1000" ><action android:name="com.dbjtech.myservice" /></intent-filter></service>【結(jié)論】目前看來,priority這個屬性貌似只適用于broadcast,對于Service來說可能無效
方法三:
提升service進程優(yōu)先級
Android中的進程是托管的,當系統(tǒng)進程空間緊張的時候,會依照優(yōu)先級自動進行進程的回收。Android將進程分為6個等級,它們按優(yōu)先級順序由高到低依次是:
?? 1.前臺進程( FOREGROUND_APP)
?? 2.可視進程(VISIBLE_APP )
?? 3. 次要服務(wù)進程(SECONDARY_SERVER )
?? 4.后臺進程 (HIDDEN_APP)
?? 5.內(nèi)容供應(yīng)節(jié)點(CONTENT_PROVIDER)
?? 6.空進程(EMPTY_APP)
當service運行在低內(nèi)存的環(huán)境時,將會kill掉一些存在的進程。因此進程的優(yōu)先級將會很重要,可以使用startForeground將service放到前臺狀態(tài)。這樣在低內(nèi)存時被kill的幾率會低一些。
在onStartCommand方法內(nèi)添加如下代碼:
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),運行時在下拉列表會看到自己的APP在:
【結(jié)論】如果在極度極度低內(nèi)存的壓力下,該service還是會被kill掉,并且不一定會restart?
保持Service不被Kill掉的方法--雙Service守護,代碼如下:
AndroidManifest.xml:
<activityandroid:name=".MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name="ServiceOne"android:process=":remote" ><intent-filter><action android:name="com.example.servicedemo.ServiceOne" /></intent-filter></service><serviceandroid:name="ServiceTwo"android:process=":remote" ><intent-filter><action android:name="com.example.servicedemo.ServiceTwo" /></intent-filter></service>MainActivity.java:
package com.example.servicedemo;import java.util.ArrayList;import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.content.Context; import android.content.Intent; import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent serviceOne = new Intent();serviceOne.setClass(MainActivity.this, ServiceOne.class);startService(serviceOne);Intent serviceTwo = new Intent();serviceTwo.setClass(MainActivity.this, ServiceTwo.class);startService(serviceTwo);}public static boolean isServiceWorked(Context context, String serviceName) {ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager.getRunningServices(Integer.MAX_VALUE);for (int i = 0; i < runningService.size(); i++) {if (runningService.get(i).service.getClassName().toString().equals(serviceName)) {return true;}}return false;} }ServiceOne.java:
package com.example.servicedemo;import java.util.Timer; import java.util.TimerTask;import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log;public class ServiceOne extends Service {public final static String TAG = "com.example.servicedemo.ServiceOne";@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand");thread.start();return START_STICKY;}Thread thread = new Thread(new Runnable() {@Overridepublic void run() {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {Log.e(TAG, "ServiceOne Run: "+System.currentTimeMillis());boolean b = MainActivity.isServiceWorked(ServiceOne.this, "com.example.servicedemo.ServiceTwo");if(!b) {Intent service = new Intent(ServiceOne.this, ServiceTwo.class);startService(service);Log.e(TAG, "Start ServiceTwo");}}};timer.schedule(task, 0, 1000);}});@Overridepublic IBinder onBind(Intent arg0) {return null;}}ServiceTwo.java:
package com.example.servicedemo;import java.util.Timer; import java.util.TimerTask;import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log;public class ServiceTwo extends Service {public final static String TAG = "com.example.servicedemo.ServiceTwo";@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand");thread.start();return START_REDELIVER_INTENT;}Thread thread = new Thread(new Runnable() {@Overridepublic void run() {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {Log.e(TAG, "ServiceTwo Run: " + System.currentTimeMillis());boolean b = MainActivity.isServiceWorked(ServiceTwo.this, "com.example.servicedemo.ServiceOne");if(!b) {Intent service = new Intent(ServiceTwo.this, ServiceOne.class);startService(service);}}};timer.schedule(task, 0, 1000);}});@Overridepublic IBinder onBind(Intent arg0) {return null;}}第二部分:
做過android開發(fā)的人應(yīng)該都知道應(yīng)用會在系統(tǒng)資源匱乏的情況下被系統(tǒng)殺死!當后臺的應(yīng)用被系統(tǒng)回收之后,如何重新恢復(fù)它呢?網(wǎng)上對此問題有很多的討論。這里先總結(jié)一下網(wǎng)上流傳的各種解決方案,看看這些辦法是不是真的可行。
1.提高優(yōu)先級
這個辦法對普通應(yīng)用而言,應(yīng)該只是降低了應(yīng)用被殺死的概率,但是如果真的被系統(tǒng)回收了,還是無法讓應(yīng)用自動重新啟動!
2.讓service.onStartCommand返回START_STICKY
通過實驗發(fā)現(xiàn),如果在adb shell當中kill掉進程模擬應(yīng)用被意外殺死的情況(或者用360手機衛(wèi)士進行清理操作),如果服務(wù)的onStartCommand返回START_STICKY,在eclipse的進程管理器中會發(fā)現(xiàn)過一小會后被殺死的進程的確又會出現(xiàn)在任務(wù)管理器中,貌似這是一個可行的辦法。但是如果在系統(tǒng)設(shè)置的App管理中選擇強行關(guān)閉應(yīng)用,這時候會發(fā)現(xiàn)即使onStartCommand返回了START_STICKY,應(yīng)用還是沒能重新啟動起來!
3.android:persistent="true"
網(wǎng)上還提出了設(shè)置這個屬性的辦法,通過實驗發(fā)現(xiàn)即使設(shè)置了這個屬性,應(yīng)用程序被kill之后還是不能重新啟動起來的!
4.讓應(yīng)用成為系統(tǒng)應(yīng)用
實驗發(fā)現(xiàn)即使成為系統(tǒng)應(yīng)用,被殺死之后也不能自動重新啟動。但是如果對一個系統(tǒng)應(yīng)用設(shè)置了persistent="true",情況就不一樣了。實驗表明對一個設(shè)置了persistent屬性的系統(tǒng)應(yīng)用,即使kill掉會立刻重啟。一個設(shè)置了persistent="true"的系統(tǒng)應(yīng)用,在android中具有core service優(yōu)先級,這種優(yōu)先級的應(yīng)用對系統(tǒng)的low memory killer是免疫的!
OK,說了半天,只有core service優(yōu)先級的應(yīng)用才能保證在被意外殺死之后做到立刻滿血復(fù)活。而普通應(yīng)用要想成為系統(tǒng)應(yīng)用就必須要用目標機器的簽名文件進行簽名,但這樣又造成了應(yīng)用無法保證兼容所有不同廠商的產(chǎn)品。那么該怎么辦呢?這里就來說一說雙進程守護。網(wǎng)上也有人提到過雙進程守護的辦法,但是很少能搜索到類似的源碼!如果從進程管理器重觀察會發(fā)現(xiàn)新浪微博或者360衛(wèi)視都有兩個相關(guān)的進程,其中一個就是守護進程,由此可以猜到這些商業(yè)級的軟件也采用了雙進程守護的辦法。
什么是雙進程守護呢?顧名思義就是兩個進程互相監(jiān)視對方,發(fā)現(xiàn)對方掛掉就立刻重啟!不知道應(yīng)該把這樣的一對進程是叫做相依為命呢還是難兄難弟好呢,但總之,雙進程守護的確是一個解決問題的辦法!相信說到這里,很多人已經(jīng)迫切的想知道如何實現(xiàn)雙進程守護了。這篇文章就介紹一個用NDK來實現(xiàn)雙進程保護的辦法,不過首先說明一點,下面要介紹的方法中,會損失不少的效率,反應(yīng)到現(xiàn)實中就是會使手機的耗電量變大!但是這篇文章僅僅是拋磚引玉,相信看完之后會有更多高人指點出更妙的實現(xiàn)辦法。
需要了解些什么?
這篇文章中實現(xiàn)雙進程保護的方法基本上是純的NDK開發(fā),或者說全部是用C++來實現(xiàn)的,需要雙進程保護的程序,只需要在程序的任何地方調(diào)用一下JAVA接口即可。下面幾個知識點是需要了解的:
1.Linux中多進程;
2.unix domain套接字實現(xiàn)跨進程通信;
3.linux的信號處理;
4.exec函數(shù)族的用法;
其實這些東西本身并不是多復(fù)雜的技術(shù),只是我們把他們組合起來實現(xiàn)了一個雙進程守護而已,沒有想象中那么神秘!在正式貼出代碼之前,先來說說幾個實現(xiàn)雙進程守護時的關(guān)鍵點:
1.父進程如何監(jiān)視到子進程(監(jiān)視進程)的死亡?
很簡單,在linux中,子進程被終止時,會向父進程發(fā)送SIG_CHLD信號,于是我們可以安裝信號處理函數(shù),并在此信號處理函數(shù)中重新啟動創(chuàng)建監(jiān)視進程;
2.子進程(監(jiān)視進程)如何監(jiān)視到父進程死亡?
當父進程死亡以后,子進程就成為了孤兒進程由Init進程領(lǐng)養(yǎng),于是我們可以在一個循環(huán)中讀取子進程的父進程PID,當變?yōu)?就說明其父進程已經(jīng)死亡,于是可以重啟父進程。這里因為采用了循環(huán),所以就引出了之前提到的耗電量的問題。
3.父子進程間的通信
有一種辦法是父子進程間建立通信通道,然后通過監(jiān)視此通道來感知對方的存在,這樣不會存在之前提到的耗電量的問題,在本文的實現(xiàn)中,為了簡單,還是采用了輪詢父進程PID的辦法,但是還是留出了父子進程的通信通道,雖然暫時沒有用到,但可備不時之需!
騰訊的面試官問我:應(yīng)用程序死了如何恢復(fù)?確實,雙進程守護只能做到進程被殺死后重新啟動,但是重啟后如何恢復(fù)到之前的狀態(tài)這是一個問題。因為進程被意外殺死的情況,onSaveInstance是來不及執(zhí)行的,所以程序的狀態(tài)沒法保存!對于雙進程守護來說,不知道是不是可以再父進程進入后臺以后(onStop),把數(shù)據(jù)收集起來保存到子進程中,然后父進程重啟以后從子進程中取出這些信息呢?這是一個辦法,但是上面說明的雙進程守護程序的實現(xiàn)中還做不到,因為父進程重啟以后,子進程也掛掉重新建立了,要想實現(xiàn)優(yōu)雅的恢復(fù),還得在做出點改進才是!只能實時保存數(shù)據(jù)到數(shù)據(jù)庫等。
參考:
Android實現(xiàn)雙進程守護 - 天山折梅 - 博客頻道 - CSDN.NET
http://blog.csdn.net/ztemt_sw2/article/details/27101681
轉(zhuǎn)載于:https://www.cnblogs.com/zhujiabin/p/6073529.html
總結(jié)
以上是生活随笔為你收集整理的Android 保持Service不被Kill掉的方法--双Service守护 Android实现双进程守护的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 使用c#开发Fildder插件抓取解析w
- 下一篇: echarts 的柱状图 填充色为透明
