本文分爲兩個部分,第一部分爲雙Service守護,第二部分爲雙進程守護java
第一部分:mysql
1、Service簡介:
Java.lang.Objectlinux
↳Android.content.Contextandroid
↳android.content.ContextWrapper面試
↳android.app.Servicesql
Service是應用程序Application的一個組件(component)。
它的做用有兩點:1.用來提供一個長期在後臺運行而且不與用戶交互的操做,2.也能夠爲其餘應用程序提供服務。
Service必須和其餘四大組件同樣,使用<service>標籤在AndroidManifest.xml中進行聲明。
啓動service有兩種方式Context.startService() 和 Context.bindService()。shell
注意,除了特別指定外,service並非單獨的進程,通常service在其宿主進程的主線程(UI Thread)中運行【固然也能夠在新的線程中startService,這樣Service就不是在MainThread了】。這意味着,若是您的服務要作任何 耗時(如 MP3 播放) 或阻塞 (好比網絡) 操做,它應該產生它本身的線程,用來作那項工做。(service不是單獨的進程也不是單獨的線程)數據庫
Service提供了兩大功能:
Context.startService()用來在後臺啓動一個服務;
Context.bindService()用來綁定其餘服務,以此來獲取其餘service提供的服務;網絡
本地服務 Local Service 用於應用程序內部app
它能夠啓動並運行,直至有人中止了它或它本身中止。在這種方式下,它以調用Context.startService()啓動,而以調用Context.stopService()結束。它能夠調用Service.stopSelf() 或 Service.stopSelfResult()來本身中止。不論調用了多少次startService()方法,你只須要調用一次stopService()來中止服務。
【用於實現應用程序本身的一些耗時任務,好比查詢升級信息,並不佔用應用程序好比Activity所屬線程,而是單開線程後臺執行,這樣用戶體驗比較好】
遠程服務 Remote Service 用於android系統內部的應用程序之間
它能夠經過本身定義並暴露出來的接口進行程序操做。客戶端創建一個到服務對象的鏈接,並經過那個鏈接來調用服務。鏈接以調用Context.bindService()方法創建,以調用 Context.unbindService()關閉。多個客戶端能夠綁定至同一個服務。若是服務此時尚未加載,bindService()會先加載它。
【可被其餘應用程序複用,好比天氣預報服務,其餘應用程序不須要再寫這樣的服務,調用已有的便可】
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()方法。
(注意這個新老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.
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等運行在相同的進程中,那麼將會增長該進程的重要性。
4、保持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方法幾個返回值簡介:
一、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。
@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>
【結論】目前看來,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方法內添加以下代碼:
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在:
【結論】若是在極度極度低內存的壓力下,該service仍是會被kill掉,而且不必定會restart
保持Service不被Kill掉的方法--雙Service守護,代碼以下:
AndroidManifest.xml:
<activity android: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> <service android:name="ServiceOne" android:process=":remote" > <intent-filter> <action android:name="com.example.servicedemo.ServiceOne" /> </intent-filter> </service> <service android: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 { @Override protected 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"; @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStartCommand"); thread.start(); return START_STICKY; } Thread thread = new Thread(new Runnable() { @Override public void run() { Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public 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); } }); @Override public 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"; @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStartCommand"); thread.start(); return START_REDELIVER_INTENT; } Thread thread = new Thread(new Runnable() { @Override public void run() { Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public 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); } }); @Override public IBinder onBind(Intent arg0) { return null; } }
第二部分:
作過android開發的人應該都知道應用會在系統資源匱乏的狀況下被系統殺死!當後臺的應用被系統回收以後,如何從新恢復它呢?網上對此問題有不少的討論。這裏先總結一下網上流傳的各類解決方案,看看這些辦法是否是真的可行。
1.提升優先級
這個辦法對普通應用而言,應該只是下降了應用被殺死的機率,可是若是真的被系統回收了,仍是沒法讓應用自動從新啓動!
2.讓service.onStartCommand返回START_STICKY
經過實驗發現,若是在adb shell當中kill掉進程模擬應用被意外殺死的狀況(或者用360手機衛士進行清理操做),若是服務的onStartCommand返回START_STICKY,在eclipse的進程管理器中會發現過一小會後被殺死的進程的確又會出如今任務管理器中,貌似這是一個可行的辦法。可是若是在系統設置的App管理中選擇強行關閉應用,這時候會發現即便onStartCommand返回了START_STICKY,應用仍是沒能從新啓動起來!
3.android:persistent="true"
網上還提出了設置這個屬性的辦法,經過實驗發現即便設置了這個屬性,應用程序被kill以後仍是不能從新啓動起來的!
4.讓應用成爲系統應用
實驗發現即便成爲系統應用,被殺死以後也不能自動從新啓動。可是若是對一個系統應用設置了persistent="true",狀況就不同了。實驗代表對一個設置了persistent屬性的系統應用,即便kill掉會馬上重啓。一個設置了persistent="true"的系統應用,在android中具備core service優先級,這種優先級的應用對系統的low memory killer是免疫的!
OK,說了半天,只有core service優先級的應用才能保證在被意外殺死以後作到馬上滿血復活。而普通應用要想成爲系統應用就必需要用目標機器的簽名文件進行簽名,但這樣又形成了應用沒法保證兼容全部不一樣廠商的產品。那麼該怎麼辦呢?這裏就來講一說雙進程守護。網上也有人提到過雙進程守護的辦法,可是不多能搜索到相似的源碼!若是從進程管理器重觀察會發現新浪微博或者360衛視都有兩個相關的進程,其中一個就是守護進程,由此能夠猜到這些商業級的軟件也採用了雙進程守護的辦法。
什麼是雙進程守護呢?顧名思義就是兩個進程互相監視對方,發現對方掛掉就馬上重啓!不知道應該把這樣的一對進程是叫作相依爲命呢仍是難兄難弟好呢,但總之,雙進程守護的確是一個解決問題的辦法!相信說到這裏,不少人已經迫切的想知道如何實現雙進程守護了。這篇文章就介紹一個用NDK來實現雙進程保護的辦法,不過首先說明一點,下面要介紹的方法中,會損失很多的效率,反應到現實中就是會使手機的耗電量變大!可是這篇文章僅僅是拋磚引玉,相信看完以後會有更多高人指點出更妙的實現辦法。
須要瞭解些什麼?
這篇文章中實現雙進程保護的方法基本上是純的NDK開發,或者說所有是用C++來實現的,須要雙進程保護的程序,只須要在程序的任何地方調用一下JAVA接口便可。下面幾個知識點是須要了解的:
1.Linux中多進程;
2.unix domain套接字實現跨進程通訊;
3.linux的信號處理;
4.exec函數族的用法;
其實這些東西自己並非多複雜的技術,只是咱們把他們組合起來實現了一個雙進程守護而已,沒有想象中那麼神祕!在正式貼出代碼以前,先來講說幾個實現雙進程守護時的關鍵點:
1.父進程如何監視到子進程(監視進程)的死亡?
很簡單,在linux中,子進程被終止時,會向父進程發送SIG_CHLD信號,因而咱們能夠安裝信號處理函數,並在此信號處理函數中從新啓動建立監視進程;
2.子進程(監視進程)如何監視到父進程死亡?
當父進程死亡之後,子進程就成爲了孤兒進程由Init進程領養,因而咱們能夠在一個循環中讀取子進程的父進程PID,當變爲1就說明其父進程已經死亡,因而能夠重啓父進程。這裏由於採用了循環,因此就引出了以前提到的耗電量的問題。
3.父子進程間的通訊
有一種辦法是父子進程間創建通訊通道,而後經過監視此通道來感知對方的存在,這樣不會存在以前提到的耗電量的問題,在本文的實現中,爲了簡單,仍是採用了輪詢父進程PID的辦法,可是仍是留出了父子進程的通訊通道,雖然暫時沒有用到,但可備不時之需!
騰訊的面試官問我:應用程序死了如何恢復?確實,雙進程守護只能作到進程被殺死後從新啓動,可是重啓後如何恢復到以前的狀態這是一個問題。由於進程被意外殺死的狀況,onSaveInstance是來不及執行的,因此程序的狀態無法保存!對於雙進程守護來講,不知道是否是能夠再父進程進入後臺之後(onStop),把數據收集起來保存到子進程中,而後父進程重啓之後從子進程中取出這些信息呢?這是一個辦法,可是上面說明的雙進程守護程序的實現中還作不到,由於父進程重啓之後,子進程也掛掉從新創建了,要想實現優雅的恢復,還得在作出點改進纔是!只能實時保存數據到數據庫等。
參考:
Android實現雙進程守護 - 天山折梅 - 博客頻道 - CSDN.NET
http://blog.csdn.net/ztemt_sw2/article/details/27101681