1. 程式人生 > >Android程序保活總結

Android程序保活總結

最近學習了關於Android程序保活的相關知識,在此寫下一篇部落格總結下。

如果需要保證我們的應用所在程序存活,那麼我們就應該先了解一下Android系統中程序的分類。

在Android中,程序依據重要性被分為5級,越高階的程序越重要,在記憶體不夠回收程序時也會越晚被回收:

前臺程序(Foreground process):指使用者當前操作必須的程序。一般來說,系統中僅存在極少的前臺程序,而且它們會到最後才被回收掉。擁有以下特徵的程序被視為前臺程序:

擁有Activity執行在螢幕最前端的程序。(已呼叫Activity的onResume方法)

擁有正在執行的BroadcastReceiver的程序。(已呼叫BroadcastReceiver的onReceive方法)

擁有正在執行的Service的回撥方法的程序。(Service的onCreate、onStart、onDestroy方法)

可見程序(Visible process):正在執行某些使用者可見操作的程序。當殺死這些程序時,使用者會有一定的影響。擁有以下特徵的程序被視為可見程序:

擁有可視的,但不是在前臺的Activity。(已呼叫onPause方法)(當另一個個Activity設定為dialog模式時可能出現這種情況)

擁有一個前臺Service。(通過用startForeground)

擁有系統關鍵特性的Service,如動態桌布、輸入法等服務。

服務程序(Service process):擁有一個正在執行的Service的程序。通常這種程序都是不可見的,會在執行較長的時候後考慮降級回收掉。

後臺程序(Background process):這是一種對使用者體驗沒有直接影響的程序,系統會在需要記憶體的時候隨時回收這種程序,這種程序通常會持有一個已呼叫onStop方法的Activity。

空程序(Empty process):不含任何活動應用元件的程序。保留這種程序的的唯一目的是用作快取,以縮短下次在其中執行元件所需的啟動時間。 為使總體系統資源在程序快取和底層核心快取之間保持平衡,系統往往會終止這些程序。

更多詳細的程序優先順序內容可以參考Android官方文件:

眾所周知,Android是基於Linux系統的。在Android程序回收策略中,Android程序與Linux程序根據OOM_ADJ閾值進行區分:

OOM_ADJ >= 4:比較容易被殺死的程序

OOM_ADJ 0 ~ 3:不容易被殺死的程序

OOM_ADJ < 0 :純Linux程序,非Android程序

當Android系統察覺裝置記憶體不足時,會按照閾值從大到小殺死程序。

具體的oom_adj值的意義我們可以檢視AOSP中的com.android.server.am.ProcessList 檔案(其中本人添加了一些中文註釋):

/**

* Activity manager code dealing with processes.

*/

finalclassProcessList {

...

// OOM adjustments for processes in various states:

// Adjustment used in certain places where we don't know it yet.

// (Generally this is something that is going to be cached, but we

// don't know the exact value in the cached range to assign yet.)

// 未知程序,通常是用作快取

staticfinalintUNKNOWN_ADJ =16;

// This is a process only hosting activities that are not visible,

// so it can be killed without any disruption.

// 擁有不可視的Activity的程序,可以不影響影響使用者的情況下殺掉

staticfinalintCACHED_APP_MAX_ADJ =15;

staticfinalintCACHED_APP_MIN_ADJ =9;

// The B list of SERVICE_ADJ -- these are the old and decrepit

// services that aren't as shiny and interesting as the ones in the A list.

// 一些舊的服務程序

staticfinalintSERVICE_B_ADJ =8;

// This is the process of the previous application that the user was in.

// This process is kept above other things, because it is very common to

// switch back to the previous app.  This is important both for recent

// task switch (toggling between the two top recent apps) as well as normal

// UI flow such as clicking on a URI in the e-mail app to view in the browser,

// and then pressing back to return to e-mail.

// 使用者使用的前一個程序

staticfinalintPREVIOUS_APP_ADJ =7;

// This is a process holding the home application -- we want to try

// avoiding killing it, even if it would normally be in the background,

// because the user interacts with it so much.

// 主介面程序

staticfinalintHOME_APP_ADJ =6;

// This is a process holding an application service -- killing it will not

// have much of an impact as far as the user is concerned.

// 持有應用服務的程序

staticfinalintSERVICE_ADJ =5;

// This is a process with a heavy-weight application.  It is in the

// background, but we want to try to avoid killing it.  Value set in

// system/rootdir/init.rc on startup.

// 重量級應用程序

staticfinalintHEAVY_WEIGHT_APP_ADJ =4;

// This is a process currently hosting a backup operation.  Killing it

// is not entirely fatal but is generally a bad idea.

// 執行備份操作的程序

staticfinalintBACKUP_APP_ADJ =3;

// This is a process only hosting components that are perceptible to the

// user, and we really want to avoid killing them, but they are not

// immediately visible. An example is background music playback.

// 擁有使用者可感知元件的程序

staticfinalintPERCEPTIBLE_APP_ADJ =2;

// This is a process only hosting activities that are visible to the

// user, so we'd prefer they don't disappear.

// 擁有使用者僅可見、不可互動的Activity的程序

staticfinalintVISIBLE_APP_ADJ =1;

// This is the process running the current foreground app.  We'd really

// rather not kill it!

// 前臺執行的程序

staticfinalintFOREGROUND_APP_ADJ =0;

// This is a system persistent process, such as telephony.  Definitely

// don't want to kill it, but doing so is not completely fatal.

// 系統常駐程序

staticfinalintPERSISTENT_PROC_ADJ = -12;

// The system process runs at the default adjustment.

// 系統程序

staticfinalintSYSTEM_ADJ = -16;

// Special code for native processes that are not being managed by the system (so

// don't have an oom adj assigned by the system).

// 為native程序保留,他們不被系統管理

staticfinalintNATIVE_ADJ = -17;

...

}

一般來說,Android程序被殺死有以下幾種情況:

觸發系統程序管理機制回收(Lowmemorykiller):這種方法會按照閾值從大到小進行清理

被沒有進行Root的第三方應用殺死(使用killBackgroundProcess方法):這種方法只能殺死OOM_ADJ為4以上的程序

被進行Root的第三方應用殺死(使用force-stop或者kill):理論上來說可以殺死所有程序,但一般只會清理非系統關鍵程序和非前臺可見程序

廠商的殺程序功能(force-stop或者kill):理論上來說可以殺死所有程序,包括Linux原生程序

使用者主動“強行停止”程序(force-stop):只能停用第三方和非system/phone程序應用(停用system程序應用會造成Android系統重啟)

在瞭解完Android程序的優先順序與Android程序的回收策略後,我們保活Android程序的思路就有了兩方面:

通過提升Android程序優先順序,使得程序更難以被回收

通過某些特殊的機制,在程序死後將其拉活

在某些啟用後臺服務場景中,為了防止我們的應用被第三方應用或系統管理工具在鎖屏後為省電而被殺死,我們可以通過啟動一畫素大小的介面來提升程序等級,讓程序等級從後臺程序提升到前臺程序。

1畫素Activity:

publicclassOnePixelActivityextendsActivity {

privatestaticfinalString TAG ="MyLog";

publicstaticOnePixelActivity instance =null;

@Override

protectedvoidonCreate(@NullableBundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_one_pixel);

Window window = getWindow();

// 放在左上角

window.setGravity(Gravity.START | Gravity.TOP);

WindowManager.LayoutParams layoutParams = window.getAttributes();

// 寬高為1px

layoutParams.width =1;

layoutParams.height =1;

// 起始座標

layoutParams.x =0;

layoutParams.y =0;

window.setAttributes(layoutParams);

instance =this;

Log.d(TAG,"activity onCreate");

}

@Override

protectedvoidonDestroy() {

instance =null;

Log.d(TAG,"activity onDestroy");

super.onDestroy();

}

}

編寫廣播接收器監聽鎖屏和解鎖action:

publicclassScreenBroadcastReceiverextendsBroadcastReceiver {

privatestaticfinalString TAG ="MyLog";

@Override

publicvoidonReceive(Context context, Intent intent) {

String action = intent.getAction();

switch(action) {

caseIntent.ACTION_SCREEN_ON: {//

Log.d(TAG,"screen_on");

// 關閉一畫素Activity

if(OnePixelActivity.instance !=null) {

OnePixelActivity.instance.finish();

}

break;

}

caseIntent.ACTION_SCREEN_OFF: {

Log.d(TAG,"screen_off");

// 開啟一畫素Activity

Intent activityIntent =newIntent(context, OnePixelActivity.class);

activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

context.startActivity(activityIntent);

break;

}

default:

break;

}

}

}

值得注意的是Intent.ACTION_SCREEN_ON與Intent.ACTION_SCREEN_OFF只有通過Context.registerReceiver方法註冊的廣播接收器才能監聽到,官方解釋如下:


SCREEN_ON螢幕亮起同上,在此就不給出展示了。

下面給出Service的例子,我們在啟動服務時使用registerReceiver註冊監聽器,然後在登出服務時登出監聽器:

publicclassWorkServiceextendsService {

privatestaticfinalString TAG ="MyLog";

privateScreenBroadcastReceiver receiver;

@Nullable

@Override

publicIBinder onBind(Intent intent) {

returnnull;

}

@Override

publicvoidonCreate() {

super.onCreate();

Log.d(TAG,"service onCreate");

receiver =newScreenBroadcastReceiver();

IntentFilter intentFilter =newIntentFilter();

intentFilter.addAction(Intent.ACTION_SCREEN_ON);

intentFilter.addAction(Intent.ACTION_SCREEN_OFF);

registerReceiver(receiver, intentFilter);

}

@Override

publicvoidonDestroy() {

Log.d(TAG,"service onDestroy");

unregisterReceiver(receiver);

super.onDestroy();

}

}

主Activity啟動服務後關閉自身,模擬沒有Activity的情況:

publicclassMainActivityextendsAppCompatActivity {

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Intent intent =newIntent(this, WorkService.class);

startService(intent);

finish();

}

}

通過adb shell可以看到,在鎖屏前應用所處的程序oom_adj值是較高的,鎖屏後由於啟動了Activity,oom_adj值降低了,程序的等級得到了相應的提高,變得更難以被回收了,這樣可以一定程度上緩解我們的應用被第三方應用或系統管理工具在鎖屏後為省電而被殺死的情況:



與第一種方法相似,這種方法也適用於Service在後臺提供服務的場景。由於沒有Activity的緣故,我們Service所在程序的oom_adj值通常是較高的,程序等級較低,容易被系統回收記憶體時清理掉。這時我們可以通過startForeground方法,把我們的服務提升為前臺服務,提高程序的等級。但提升為前臺服務必須繫結一個相應的Notification,這是我們不願意看到的。此時我們可以先使用一個Fake Service來繫結某一個Notification,然後利用相同的id繫結我們真正的Service,然後關閉我們的Fake Service,此時我們的Notification會隨著我們的Fake Service一齊關閉,但我們真正的Service仍依然處於前臺執行狀態,程序等級就得到了相應的提升。

FakeService程式碼:

publicclassFakeServiceextendsService {

privatestaticfinalString TAG ="MyLog";

publicstaticFakeService instance =null;

@Nullable

@Override

publicIBinder onBind(Intent intent) {

returnnull;

}

@Override

publicvoidonCreate() {

super.onCreate();

Log.d(TAG,"fake service onCreate");

// 儲存例項

instance =this;

// 開啟服務前臺執行

Notification.Builder builder =newNotification.Builder(this);

builder.setSmallIcon(R.mipmap.ic_launcher)

.setContentTitle("fake")

.setContentText("I am fake")

.setWhen(System.currentTimeMillis());

startForeground(1, builder.build());

// 開啟真正工作的Service

Intent intent =newIntent(this, WorkService.class);

startService(intent);

相關推薦

Android程序總結

最近學習了關於Android程序保活的相關知識,在此寫下一篇部落格總結下。如果需要保證我們的應用所在程序存活,那麼我們就應該先了解一下Android系統中程序的分類。在Android中,程序依據重要性被分為5級,越高階的程序越重要,在記憶體不夠回收程序時也會越晚被回收:前臺程

android程序兩年實戰經驗(已經上線使用)

程序保活參考: https://www.jianshu.com/p/53c4d8303e19 https://github.com/08carmelo/android-keeplive 以上地址這個我是通過鴻洋的公眾號看到的,我們公司做的是VPN撥號必須要求app保持後臺執行,上面連

Android程序相關實踐

最近測試APP時接到個需求:1畫素保活 打眼一看,應該跟安卓程序有關係,索性找點詳細的資料來了解下: 系統什麼時候殺掉一個程序?為什麼殺掉這個程序?怎樣最大程度保活一個程序?詳細的講解可以參考這個連結------https://www.cnblogs.com/Doing-what-I-love/p/553

Android 程序資料

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

【騰訊Bugly乾貨分享】Android程序招式大全

【騰訊Bugly乾貨分享】Android程序保活招式大全 本文來自於騰訊bugly開發者社群,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8 作者:騰訊——張興華 目前市面上的應用,貌似除了微信和手Q都會

Android程序招數概覽

Android中的程序保活應該分為兩個方面: 提高程序的優先順序,減少被系統殺死的可能性 在程序已經被殺死的情況下,通過一些手段來重新啟動應用程序 本文針對這兩方面來程序闡述,並給出相應的示例。其實主要也是在前人的基礎上做了一個總結,並進行了一些實踐。 閱讀本

android程序實戰(已經上線使用)

https://github.com/08carmelo/android-keeplive 這個我是通過鴻洋的公眾號看到的,我們公司做的是vpn撥號必須要求app保持後臺執行,上面介紹的很詳細,我用的github中使用的程式碼 這個具體需要你自己看 我使用後其實還是有

Android程序招式大全

作者:騰訊——張興華目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死問題。本文對 Android 程序拉活進行一個總結。Android 程序拉活包括兩個層面:A. 提供程序優先順序,降低程序被殺死的概率B. 在程序被殺死後,進行拉活本文下面就從這兩

android程序實踐(根據鴻洋大神彙總,本人忘性大備份下)

前言程序保活的關鍵點有兩個,一個是程序優先順序的理解,優先順序越高存活機率越大。二是弄清楚哪些場景會導致程序會kill,然後採取下面的策略對各種場景進行優化:提高程序的優先順序在程序被kill之後能夠喚醒程序優先順序Android一般的程序優先順序劃分:1.前臺程序 (Foreground process)2

Android 程序資料彙總與華為白名單那些事

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

Android程序(常駐記憶體)

Android將程序分為6個等級,它們按優先順序順序由高到低依次是:  1.前臺程序( FOREGROUND_APP);  2.可視程序(VISIBLE_APP );  3. 次要服務程序(SECONDARY_SERVER );  4.後臺程序 (HIDDEN_APP);  

Android程序精煉詳解

一、前期基礎知識儲備在之前的文章《如何保證Service在後臺不被殺死?》中,筆者分析了為什麼要保活Service、Service的幾種保活方法和Service保活的意義。今天的這篇文章就更進一步,講解程序保活的方法和意義。(1)什麼是程序保活?拿我們的手機應用程式QQ來說,

Android程序(最新)帶你淺析這幾種可行性的方案

1.概述   據前人驗證,在沒有白名單的情況下,安卓系統要做一個任何情況下都不被殺死的應用是基本不可能的,但是我們可以做到應用基本不被殺死,如果殺死可以立即復活.經過上網查詢,程序常駐的方案眾說紛紜,但是很多的方案都是不靠譜的或不是最好的,結合很多資料,今天總結一下And

android程序

有些時候(國內通常是這樣)我們需要應用在 後臺存活,但是現在的很多手機room是在記憶體不足或者一定時間後銷燬程序的。 總結幾種android常用的保活手段。 系統出於體驗和效能上的考慮,app在退到後臺時系統並不會真正的kill掉這個程序,而是將其快取起來。開啟的應用越多

關於 Android 程序,你所需要知道的一切

早前,我在知乎上回答了這樣一個問題:怎麼讓 Android 程式一直後臺執行,像 QQ 一樣不被殺死?。關於 Android 平臺的程序保活這一塊,想必是所有 Android 開發者矚目的內容之一。你到網上搜 Android 程序保活,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,Gith

Android 程序,Service程序常駐

關於 Android 平臺的程序保活這一塊,想必是所有 Android 開發者矚目的內容之一。你到網上搜 Android 程序保活,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,Github還出現了一個很火的“黑科技”程序保活庫,聲稱可以做到

android程序-前臺server+畫素

借鑑部分 背景:從產品的角度來說,任何一個應用程式的PM都希望自己的應用程式在使用者手機中的留存率高些些,之前我接觸到的一個業務需求也是如此,要求提升應用程式在國內第三方廠商ROM中的存活率。 如前篇所述踩坑篇,保活策略只在android原生系統中起作用,在國內第三方廠商R

Android程序-自“裁”或者耍流氓

本篇文章是後臺殺死系列的最後一篇,主要探討一下程序的保活,Android本身設計的時候是非常善良的,它希望程序在不可見或者其他一些場景下APP要懂得主動釋放,可是Android低估了”貪婪“,尤其是很多國產APP,只希望索取來提高自己的效能,不管其他APP或者系

【騰訊Bugly乾貨分享】Android 程序招式大全

本文來自於騰訊bugly開發者社群, ,原文地址:https://segmentfault.com/a/1190000006251859作者:騰訊——張興華目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死問題。本文對 Android 程序拉活進行一

Android 程序--1畫素

在Android開發中,必定有一些應用是需要常駐後臺執行的,比如長期對某個事物的監聽或者長期掃描等等。如果Android手機鎖屏了,就有一定機率會給手機廠商的OS系統給殺死。所以,為了在手機鎖屏之後避免應用給殺死,我們可以選擇提高程序的優先順序,所以使用1畫素A