如何讓我們的Android應用程式保活?
一、執行緒 程式 應用
預設下,同一個應用的所有元件都執行在同一個程式中。當然也可以在manifest清單檔案中設定元件執行的程式。
元件元素 activity、service、receiver、provider,都有一個process屬性可以指定元件執行在哪個程式中。這個屬性可以設定為每個元件執行在自己的程式中,或者設定程式同名與其他一些元件共享一個程式。
Android會在某些時刻決定關閉一個程式,比如記憶體剩餘較小並且其他程式更迫切需要記憶體時,某些程式會被關閉時,且程式中的元件們都被銷燬.如果重新需要這些元件工作時,程式又會被建立出來。
系統不會為每個元件的例項分別建立執行緒。所有執行於一個程式的元件都在主執行緒(UI執行緒)中被例項化,並且系統對每個元件的呼叫都在這個執行緒中派發,且只能在UI執行緒中管理的你的介面。
可以 執行緒 程式 應用 打個比方:
執行緒(Thread):流水線 、 程式(process): 車間 、 應用:工廠
為什麼使用多執行緒?
執行緒如同一條流水線,當我們執行一些比如網路連線或資料庫請求這樣的耗時操作,會造成該執行緒阻塞。而我們又需要在主執行緒(UI執行緒)中對整個介面進行響應。所以為了不阻塞UI執行緒,提高應用程式的效能,需要使用多執行緒來操作。
為什麼使用多程式?
在Android系統中,每個程式都有一個記憶體限制。如果一個應用可以多有個程式,那麼這個應用可以有更多的記憶體來執行,使我們的應用記憶體限制變大,優化程式的速度。
二、程式的等級(程式的生命週期)
一. 程式按照優先順序分為不同的等級:
前臺程式(Foreground process):
A. 擁有使用者正在互動的 Activity( onResume()狀態) B. 正在與bound繫結服務互動的 Activity C. 正在“前臺”執行的 Service(startForeground()被呼叫) D. 生命週期函式正在被執行的 Service(onCreate()、onStart() 或 onDestroy()) E. 正執行 onReceive() 方法的 BroadcastReceiver (該程式優先順序別最高,殺死前臺程式需要使用者的響應。)
可見程式(Visible process):
該程式並不是在最前端,並沒有得到焦點,但是我們卻能看到它們。
A. 擁有不在前臺、但仍對使用者可見的 Activity(例如彈出一個對話方塊的Activity) B. 繫結到可見程式(或前臺程式)中的Activity 的 Service
服務程式(Service process):
A. 正在執行的通過 startService() 啟動的,且不屬於上述兩個更高程式狀態的Service
後臺程式(Background process):
A. 不可見狀態的Activity程式
空程式(Empty process):
A. 沒有執行任何應用元件的程式,保留這個程式主要是為了快取的需要,待下次相關元件執行時直接從記憶體中獲取資料,縮短對資料獲取的時間。
程式等級判斷原則:當存在多種等級的程式狀態時,優先考慮優先順序較高的程式。
三、程式回收機制:Low Memory Killer
在Android系統,當使用者退出應用程式之後,應用程式的程式還是會存在於系統中,這樣方便於程式的再次啟動。
但隨著開啟的應用程式數量的增加,系統記憶體會變得不足,就需要殺掉一部分應用程式的程式以釋放記憶體空間。
至於是否需要殺死哪些程式需要被殺死,是通過Low Memory Killer機制來進行判定的。
Low Memory Killer是通過程式的oom_adj與佔用記憶體的大小決定要殺死的程式,oom_adj越小越不容易被殺死。
有一組系統記憶體臨界值和與之一一對應的一組oom_adj值,當系統剩餘記憶體位於記憶體臨界值中的一個範圍內時,如果一個程式的oom_adj值大於/等於這個臨界值對應的oom_adj值,該程式就會被殺掉。
四、防止程式被殺
輕量化程式:按照Low Memory Killer的殺死程式的規則,應儘量讓Service在後臺做較少的事情,且及時釋放記憶體資源。
利用service提升程式許可權: 呼叫 startForeground方法將service置為“前臺程式”,不過這樣我們需要傳送一個Notification通知。
如何取消通知: 1. 在Android 4.3之前,可以通過構造一個空的Notification使通知欄不會顯示我們傳送的Notification。 2. 在Android 4.3之後,谷歌不再允許構造空的Notification。但有一個奇葩的方法: 可以將兩個同程式的Service都通過startForeground設定為前臺程式,但它們使用的是同一個ID的Notification通知,這樣只會產生一個Notification。然後其中一個Service取消前臺通知,那麼該通知會被關閉。這樣剩下的Service還是一個前臺Service,且通知欄沒有通知。
在application標籤中加入 android:persistent=“true”
在androidmanifest.xml中的application標籤中加入android:persistent="true" 屬性後能夠達到保證該應用程式所在程式不會被LMK殺死,異常出現後也可以自啟。但前提是應用程式必須是系統應用,即應用程式不能採用通常的安裝方式。必須將應用程式的apk包直接放到/system/app目錄下。而且必須重啟系統後才能生效。
五、被殺後重啟
利用Service機制重啟:
安卓系統設計Service時,可以通過其onStartCommand方法中返回不同的值告知系統,讓系統在Service因為記憶體不足被殺掉後可以在資源不緊張時重啟。
START_NOT_STICKY 如果系統在onStartCommand()方法返回之後殺死這個服務,那麼直到接受到新的Intent物件,這個服務才會被重新建立。 START_STICKY 如果系統在onStartCommand()返回後殺死了這個服務,系統就會重新建立這個服務並且呼叫onStartCommand()方法,但是它不會重新傳遞最後的Intent物件,系統會用一個null的Intent物件來呼叫onStartCommand()方法。 在這個情況下,除非有一些被髮送的Intent物件在等待啟動服務。這適用於不執行命令的媒體播放器(或類似的服務),它只是無限期的執行著並等待工作的到來。 START_REDELIVER_INTENT 如果系統在onStartCommand()方法返回後,系統就會重新建立了這個服務,並且用傳送給這個服務的最後的Intent物件呼叫了onStartCommand()方法。任意等待中的Intent物件會依次被髮送。這適用於那些應該立即恢復正在執行的工作的服務,如下載檔案。
雖然在這種情況可以讓Service被系統重啟的,但不能立即被重啟。而且在某些定製ROM上失效。
雙程式守護:
設計AB兩個不同程式,A程式裡面輪詢檢查B程式是否存活,沒存活的話將其拉起,同樣B程式裡面輪詢檢查A程式是否存活,沒存活的話也將其拉起,而後臺邏輯可以隨便放在某個程式裡執行即可。<service android:name=".services.FirService" android:process=":fir" /> <service android:name=".services.SecService" android:process=":sec" />
使用兩個程式分別裝載兩個Service,兩個Service相互輪詢喚醒:
public class FirService extends Service { public final static String TAG = "com.example.servicedemo.FirService"; @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, "FirService Run: "+System.currentTimeMillis()); boolean b = Util.isServiceWorked(FirService.this, "com.example.servicedemo.SecService"); if(!b) { Intent service = new Intent(FirService.this, SecService.class); startService(service); Log.e(TAG, "Start SecService"); } } }; timer.schedule(task, 0, 1000); } }); @Override public IBinder onBind(Intent arg0) { return null; } } -------------------------------------- public class SecService extends Service { public final static String TAG = "com.example.servicedemo.SecService"; @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, "SecService Run: " + System.currentTimeMillis()); boolean b = Util.isServiceWorked(SecService.this, "com.example.servicedemo.FirService"); if(!b) { Intent service = new Intent(ServiceTwo.this, FirService.class); startService(service); } } }; timer.schedule(task, 0, 1000); } }); @Override public IBinder onBind(Intent arg0) { return null; } } ----------------------------------- public class Util { 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; } }
利用靜態廣播接收器重啟程式:
使用靜態註冊的廣播接收器BroadcastReceiver來監聽一些系統廣播/其他應用發出的廣播在onReceive中重啟程式。無法接受到系統?: 1. 在Android 3.1之後,處於stopped狀態的應用無法接收到系統廣播。 2. 如果一個應用在安裝後從來沒有啟動過,或者已經被使用者強制停止了,那麼這個應用就處於停止狀態(stopped state)。 3. 如果想使處於stopped狀態的應用也接收到廣播,需要在intent中增加FLAG_INCLUDE_STOPPED_PACKAGES這個Flag。要注意的是,使用者無法自定義系統廣播。 4. 有些廣播只有靜態接收器可以接收得到。 5. 深度定製ROM使應用重回STOPPED狀態
與系統Service捆綁 : NotificationListenerService
NotificationListenerService是通過系統調起的服務,當有應用發起通知的時候,系統會將通知的動作和資訊回撥給NotificationListenerService。
<service android:name=".services.NotificationService " android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:process=":service"> <intent-filter> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter> </service> ------------------- public class NotificationService extends NotificationListenerService { @Override public void onNotificationPosted(StatusBarNotification sbn) { } @Override public void onNotificationRemoved(StatusBarNotification sbn) { } }
當系統發現某應用產生通知或者使用者刪除某通知,都會回撥該服務的上述這兩個函式,函式的引數StatusBarNotification包含著該通知的具體資訊。.
其他:全家桶/SDK喚醒、手機廠商的白名單定製
六、Android應用程式保活的權衡
程式保活要回到使用者體驗。有的程式保活不能做到效能優化,反而在不斷重啟費電、亦或如幽靈般重複出現在使用者面前。在某些應用場景裡,有的程式保活起到了應有的作用,比如音樂播放時的前臺Service…..
流氓的程式保活只會搞壞Android 生態環境,傷害 Android 平臺開發者等人的利益。當然,Google也不會讓這麼流氓方式胡作非為。
參考:
http://blog.csdn.net/aigestudio/article/details/51348408
http://www.jianshu.com/p/63aafe3c12af
http://www.cnblogs.com/angeldevil/archive/2013/05/21/3090872.html
…
相關文章
- Android應用保活實踐Android
- Android程式保活方案Android
- 程式設計師如何保證我們的程式碼質量程式設計師
- 我們是如何使用 Electron 構建 Linux 桌面應用程式的Linux
- 如何讓我們的模型更簡潔模型
- 讓應用程式禁止Windows螢幕保護 (轉)Windows
- Android程式保活-自“裁”或者耍流氓Android
- 程式保活方案
- Android之APP保活AndroidAPP
- 我的第一個Android應用程式Android
- 在Jenkins的幫助下讓我們的應用CI與CDJenkins
- 如何讓海外玩家愛上我們的遊戲?遊戲
- 微信團隊原創分享:Android版微信後臺保活實戰分享(程式保活篇)Android
- 讓我們成為更好的程式設計師程式設計師
- Android程式保活(二):利用 Notification 提升許可權Android
- 如何禁止非法授權修改,保護我們的Word文件?
- 如何應用“防呆法”的原理於我們的工作上?
- 如何讓你的iOS與Android應用與眾不同iOSAndroid
- 我們應該如何編寫高質量的前端程式碼前端
- 讓我重新活一次。..
- “旁觀者效應”是如何毀掉我們的程式碼的
- 如何釋出Android應用程式Android
- 讓我們一起搖擺,AR應用DanceReality助你記住舞步
- 【指令碼】如何確保應用程式的唯一性指令碼
- 讓我們重視程式執行效率 (轉)
- 雲應用程式如何加強隱私保護
- 如何結構化我們的程式碼
- release的時候如何把我們的版本號打包到應用中
- 我們應該如何給需求排序?排序
- 如何保護在Autodesk應用程式商店的應用不被盜版 - 1
- 讓我們用gulp寫個前端腳手架前端
- 關於 Android 程式保活,你所需要知道的一切Android
- 如何降低android應用程式的耗電量Android
- 你的APP應用讓我變“胖”了APP
- 如何構建 Android MVVM 應用程式AndroidMVVM
- 瞧,我們組的Web應用開發方式Web
- 日本AR應用於掃墓 讓逝去的人“復活”
- Nir Eyal:健身應用大部分都沒用 並且讓我們變得更胖