Android進(jìn)程毖罩瑁活方案

自己曾經(jīng)也在這個(gè)問(wèn)題上傷過(guò)腦經(jīng)集灌,前幾日剛好有一個(gè)北京的哥們?cè)赒Q說(shuō)在做IM類的項(xiàng)目,問(wèn)我進(jìn)程备炊撸活如何處理比較恰當(dāng)欣喧,決定去總結(jié)一下,網(wǎng)上搜索一下進(jìn)程常駐的方案好多好多梯找,但是很多的方案都是不靠譜的或者不是最好的唆阿,結(jié)合很多資料,今天總結(jié)一下Android進(jìn)程毙獯福活的一些方案驯鳖,都附有完整的實(shí)現(xiàn)源碼,有些可能你已經(jīng)知道久免,但是有些你可能是第一次聽說(shuō)浅辙,(1像素Activity,前臺(tái)服務(wù)阎姥,賬號(hào)同步记舆,Jobscheduler,相互喚醒,系統(tǒng)服務(wù)捆綁呼巴,如果你都了解了泽腮,請(qǐng)忽略)經(jīng)過(guò)多方面的驗(yàn)證御蒲,Android系統(tǒng)中在沒(méi)有白名單的情況下做一個(gè)任何情況下都不被殺死的應(yīng)用是基本不可能的,但是我們可以做到我們的應(yīng)用基本不被殺死诊赊,如果殺死可以馬上滿血復(fù)活厚满,原諒我講的特別含蓄,畢竟現(xiàn)在的技術(shù)防不勝防啊碧磅,不死應(yīng)用還是可能的碘箍。

有幾個(gè)問(wèn)題需要思考,系統(tǒng)為什么會(huì)殺掉進(jìn)程鲸郊,殺的為什么是我的進(jìn)程丰榴,這是按照什么標(biāo)準(zhǔn)來(lái)選擇的,是一次性干掉多個(gè)進(jìn)程严望,還是一個(gè)接著一個(gè)殺,甭呖郑活套路一堆像吻,如何進(jìn)行進(jìn)程保活才是比較恰當(dāng)......如果這些問(wèn)題你還還存在复隆,或許這篇文章可以解答拨匆。

一、進(jìn)程初步了解

每一個(gè)Android應(yīng)用啟動(dòng)后至少對(duì)應(yīng)一個(gè)進(jìn)程挽拂,有的是多個(gè)進(jìn)程惭每,而且主流應(yīng)用中多個(gè)進(jìn)程的應(yīng)用比例較大

Picture

1、如何查看進(jìn)程解基本信息

對(duì)于任何一個(gè)進(jìn)程亏栈,我們都可以通過(guò)adb shell ps|grep <package_name>的方式來(lái)查看它的基本信息

Picture
解釋
u0_a16 USER 進(jìn)程當(dāng)前用戶
3881 進(jìn)程ID
1223 進(jìn)程的父進(jìn)程ID
873024 進(jìn)程的虛擬內(nèi)存大小
37108 實(shí)際駐留”在內(nèi)存中”的內(nèi)存大小
com.wangjing.processlive 進(jìn)程名

2台腥、進(jìn)程劃分

Android中的進(jìn)程跟封建社會(huì)一樣,分了三流九等绒北,Android系統(tǒng)把進(jìn)程的劃為了如下幾種(重要性從高到低)黎侈,網(wǎng)上多位大神都詳細(xì)總結(jié)過(guò)(備注:嚴(yán)格來(lái)說(shuō)是劃分了6種)。

2.1闷游、前臺(tái)進(jìn)程(Foreground process)

場(chǎng)景:

  • 某個(gè)進(jìn)程持有一個(gè)正在與用戶交互的Activity并且該Activity正處于resume的狀態(tài)峻汉。

  • 某個(gè)進(jìn)程持有一個(gè)Service,并且該Service與用戶正在交互的Activity綁定脐往。

  • 某個(gè)進(jìn)程持有一個(gè)Service休吠,并且該Service調(diào)用startForeground()方法使之位于前臺(tái)運(yùn)行。

  • 某個(gè)進(jìn)程持有一個(gè)Service业簿,并且該Service正在執(zhí)行它的某個(gè)生命周期回調(diào)方法瘤礁,比如onCreate()、 onStart()或onDestroy()梅尤。

  • 某個(gè)進(jìn)程持有一個(gè)BroadcastReceiver蔚携,并且該BroadcastReceiver正在執(zhí)行其onReceive()方法希太。

用戶正在使用的程序,一般系統(tǒng)是不會(huì)殺死前臺(tái)進(jìn)程的酝蜒,除非用戶強(qiáng)制停止應(yīng)用或者系統(tǒng)內(nèi)存不足等極端情況會(huì)殺死誊辉。

2.2、可見進(jìn)程(Visible process)

場(chǎng)景:

  • 擁有不在前臺(tái)亡脑、但仍對(duì)用戶可見的 Activity(已調(diào)用 onPause())堕澄。

  • 擁有綁定到可見(或前臺(tái))Activity 的 Service

用戶正在使用,看得到霉咨,但是摸不著蛙紫,沒(méi)有覆蓋到整個(gè)屏幕,只有屏幕的一部分可見進(jìn)程不包含任何前臺(tái)組件,一般系統(tǒng)也是不會(huì)殺死可見進(jìn)程的途戒,除非要在資源吃緊的情況下坑傅,要保持某個(gè)或多個(gè)前臺(tái)進(jìn)程存活

2.3、服務(wù)進(jìn)程(Service process)

場(chǎng)景

  • 某個(gè)進(jìn)程中運(yùn)行著一個(gè)Service且該Service是通過(guò)startService()啟動(dòng)的喷斋,與用戶看見的界面沒(méi)有直接關(guān)聯(lián)唁毒。

在內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見進(jìn)程同時(shí)運(yùn)行的情況下,服務(wù)進(jìn)程會(huì)被殺死

2.4星爪、后臺(tái)進(jìn)程(Background process)

場(chǎng)景:

  • 在用戶按了"back"或者"home"后,程序本身看不到了,但是其實(shí)還在運(yùn)行的程序浆西,比如Activity調(diào)用了onPause方法

系統(tǒng)可能隨時(shí)終止它們,回收內(nèi)存

2.5顽腾、空進(jìn)程(Empty process)

場(chǎng)景:

  • 某個(gè)進(jìn)程不包含任何活躍的組件時(shí)該進(jìn)程就會(huì)被置為空進(jìn)程近零,完全沒(méi)用,殺了它只有好處沒(méi)壞處,第一個(gè)干它!

3、內(nèi)存閾值

上面是進(jìn)程的分類抄肖,進(jìn)程是怎么被殺的呢久信?系統(tǒng)出于體驗(yàn)和性能上的考慮,app在退到后臺(tái)時(shí)系統(tǒng)并不會(huì)真正的kill掉這個(gè)進(jìn)程漓摩,而是將其緩存起來(lái)入篮。打開的應(yīng)用越多,后臺(tái)緩存的進(jìn)程也越多幌甘。在系統(tǒng)內(nèi)存不足的情況下潮售,系統(tǒng)開始依據(jù)自身的一套進(jìn)程回收機(jī)制來(lái)判斷要kill掉哪些進(jìn)程,以騰出內(nèi)存來(lái)供給需要的app, 這套殺進(jìn)程回收內(nèi)存的機(jī)制就叫 Low Memory Killer锅风。那這個(gè)不足怎么來(lái)規(guī)定呢酥诽,那就是內(nèi)存閾值,我們可以使用cat /sys/module/lowmemorykiller/parameters/minfree來(lái)查看某個(gè)手機(jī)的內(nèi)存閾值皱埠。

Picture

注意這些數(shù)字的單位是page. 1 page = 4 kb.上面的六個(gè)數(shù)字對(duì)應(yīng)的就是(MB): 72,90,108,126,144,180肮帐,這些數(shù)字也就是對(duì)應(yīng)的內(nèi)存閥值,內(nèi)存閾值在不同的手機(jī)上不一樣,一旦低于該值,Android便開始按順序關(guān)閉進(jìn)程. 因此Android開始結(jié)束優(yōu)先級(jí)最低的空進(jìn)程,即當(dāng)可用內(nèi)存小于180MB(46080*4/1024)训枢。

讀到這里托修,你或許有一個(gè)疑問(wèn),假設(shè)現(xiàn)在內(nèi)存不足恒界,空進(jìn)程都被殺光了睦刃,現(xiàn)在要?dú)⒑笈_(tái)進(jìn)程,但是手機(jī)中后臺(tái)進(jìn)程很多十酣,難道要一次性全部都清理掉涩拙?當(dāng)然不是的,進(jìn)程是有它的優(yōu)先級(jí)的耸采,這個(gè)優(yōu)先級(jí)通過(guò)進(jìn)程的adj值來(lái)反映兴泥,它是linux內(nèi)核分配給每個(gè)系統(tǒng)進(jìn)程的一個(gè)值,代表進(jìn)程的優(yōu)先級(jí)虾宇,進(jìn)程回收機(jī)制就是根據(jù)這個(gè)優(yōu)先級(jí)來(lái)決定是否進(jìn)行回收搓彻,adj值定義在com.android.server.am.ProcessList類中,這個(gè)類路徑是${android-sdk-path}sourcesandroid-23comandroidserveramProcessList.java嘱朽。oom_adj的值越小旭贬,進(jìn)程的優(yōu)先級(jí)越高,普通進(jìn)程oom_adj值是大于等于0的燥翅,而系統(tǒng)進(jìn)程oom_adj的值是小于0的骑篙,我們可以通過(guò)cat /proc/進(jìn)程id/oom_adj可以看到當(dāng)前進(jìn)程的adj值蜕提。

Picture

看到adj值是0森书,0就代表這個(gè)進(jìn)程是屬于前臺(tái)進(jìn)程,我們按下Back鍵谎势,將應(yīng)用至于后臺(tái),再次查看

Picture

adj值變成了8,8代表這個(gè)進(jìn)程是屬于不活躍的進(jìn)程呕诉,你可以嘗試其他情況下欲险,oom_adj值是多少,但是每個(gè)手機(jī)的廠商可能不一樣须喂,oom_adj值主要有這么幾個(gè)吁断,可以參考一下。

adj級(jí)別 解釋
UNKNOWN_ADJ 16 預(yù)留的最低級(jí)別坞生,一般對(duì)于緩存的進(jìn)程才有可能設(shè)置成這個(gè)級(jí)別
CACHED_APP_MAX_ADJ 15 緩存進(jìn)程仔役,空進(jìn)程,在內(nèi)存不足的情況下就會(huì)優(yōu)先被kill
CACHED_APP_MIN_ADJ 9 緩存進(jìn)程是己,也就是空進(jìn)程
SERVICE_B_ADJ 8 不活躍的進(jìn)程
PREVIOUS_APP_ADJ 7 切換進(jìn)程
HOME_APP_ADJ 6 與Home交互的進(jìn)程
SERVICE_ADJ 5 有Service的進(jìn)程
HEAVY_WEIGHT_APP_ADJ 4 高權(quán)重進(jìn)程
BACKUP_APP_ADJ 3 正在備份的進(jìn)程
PERCEPTIBLE_APP_ADJ 2 可感知的進(jìn)程又兵,比如那種播放音樂(lè)
VISIBLE_APP_ADJ 1 可見進(jìn)程
FOREGROUND_APP_ADJ 0 前臺(tái)進(jìn)程
PERSISTENT_SERVICE_ADJ -11 重要進(jìn)程
PERSISTENT_PROC_ADJ -12 核心進(jìn)程
SYSTEM_ADJ -16 系統(tǒng)進(jìn)程
NATIVE_ADJ -17 系統(tǒng)起的Native進(jìn)程

備注:(上表的數(shù)字可能在不同系統(tǒng)會(huì)有一定的出入)

根據(jù)上面的adj值,其實(shí)系統(tǒng)在進(jìn)程回收跟內(nèi)存回收類似也是有一套嚴(yán)格的策略,可以自己去了解沛厨,大概是這個(gè)樣子的宙地,oom_adj越大,占用物理內(nèi)存越多會(huì)被最先kill掉逆皮,OK宅粥,那么現(xiàn)在對(duì)于進(jìn)程如何保活這個(gè)問(wèn)題就轉(zhuǎn)化成页屠,如何降低oom_adj的值粹胯,以及如何使得我們應(yīng)用占的內(nèi)存最少。

一辰企、進(jìn)程狈缇溃活方案

1、開啟一個(gè)像素的Activity

據(jù)說(shuō)這個(gè)是手Q的進(jìn)程崩蚊常活方案竹观,基本思想,系統(tǒng)一般是不會(huì)殺死前臺(tái)進(jìn)程的潜索。所以要使得進(jìn)程常駐臭增,我們只需要在鎖屏的時(shí)候在本進(jìn)程開啟一個(gè)Activity,為了欺騙用戶竹习,讓這個(gè)Activity的大小是1像素誊抛,并且透明無(wú)切換動(dòng)畫,在開屏幕的時(shí)候整陌,把這個(gè)Activity關(guān)閉掉拗窃,所以這個(gè)就需要監(jiān)聽系統(tǒng)鎖屏廣播,我試過(guò)了泌辫,的確好使随夸,如下。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
   }
}

如果直接啟動(dòng)一個(gè)Activity震放,當(dāng)我們按下back鍵返回桌面的時(shí)候宾毒,oom_adj的值是8,上面已經(jīng)提到過(guò)殿遂,這個(gè)進(jìn)程在資源不夠的情況下是容易被回收的≌╊酰現(xiàn)在造一個(gè)一個(gè)像素的Activity。

public class LiveActivity extends Activity {

    public static final String TAG = LiveActivity.class.getSimpleName();

    public static void actionToLiveActivity(Context pContext) {
        Intent intent = new Intent(pContext, LiveActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        pContext.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        setContentView(R.layout.activity_live);

        Window window = getWindow();
        //放在左上角
        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams attributes = window.getAttributes();
        //寬高設(shè)計(jì)為1個(gè)像素
        attributes.width = 1;
        attributes.height = 1;
        //起始坐標(biāo)
        attributes.x = 0;
        attributes.y = 0;
        window.setAttributes(attributes);

        ScreenManager.getInstance(this).setActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

為了做的更隱藏墨礁,最好設(shè)置一下這個(gè)Activity的主題幢竹,當(dāng)然也無(wú)所謂了

   <style name="LiveStyle">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowNoTitle">true</item>
   </style>

在屏幕關(guān)閉的時(shí)候把LiveActivity啟動(dòng)起來(lái),在開屏的時(shí)候把LiveActivity 關(guān)閉掉饵溅,所以要監(jiān)聽系統(tǒng)鎖屏廣播妨退,以接口的形式通知MainActivity啟動(dòng)或者關(guān)閉LiveActivity。

public class ScreenBroadcastListener {

    private Context mContext;

    private ScreenBroadcastReceiver mScreenReceiver;

    private ScreenStateListener mListener;

    public ScreenBroadcastListener(Context context) {
        mContext = context.getApplicationContext();
        mScreenReceiver = new ScreenBroadcastReceiver();
    }

    interface ScreenStateListener {

        void onScreenOn();

        void onScreenOff();
    }

    /**
     * screen狀態(tài)廣播接收者
     */
    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        private String action = null;

        @Override
        public void onReceive(Context context, Intent intent) {
            action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) { // 開屏
                mListener.onScreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
                mListener.onScreenOff();
            }
        }
    }

    public void registerListener(ScreenStateListener listener) {
        mListener = listener;
        registerListener();
    }

    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        mContext.registerReceiver(mScreenReceiver, filter);
    }
}

public class ScreenManager {

    private Context mContext;

    private WeakReference<Activity> mActivityWref;

    public static ScreenManager gDefualt;

    public static ScreenManager getInstance(Context pContext) {
        if (gDefualt == null) {
            gDefualt = new ScreenManager(pContext.getApplicationContext());
        }
        return gDefualt;
    }
    private ScreenManager(Context pContext) {
        this.mContext = pContext;
    }

    public void setActivity(Activity pActivity) {
        mActivityWref = new WeakReference<Activity>(pActivity);
    }

    public void startActivity() {
            LiveActivity.actionToLiveActivity(mContext);
    }

    public void finishActivity() {
        //結(jié)束掉LiveActivity
        if (mActivityWref != null) {
            Activity activity = mActivityWref.get();
            if (activity != null) {
                activity.finish();
            }
        }
    }
}

現(xiàn)在MainActivity改成如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final ScreenManager screenManager = ScreenManager.getInstance(MainActivity.this);
        ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
         listener.registerListener(new ScreenBroadcastListener.ScreenStateListener() {
            @Override
            public void onScreenOn() {
                screenManager.finishActivity();
            }

            @Override
            public void onScreenOff() {
                screenManager.startActivity();
            }
        });
    }
}

按下back之后,進(jìn)行鎖屏咬荷,現(xiàn)在測(cè)試一下oom_adj的值

Picture

果然將進(jìn)程的優(yōu)先級(jí)提高了冠句。

但是還有一個(gè)問(wèn)題,內(nèi)存也是一個(gè)考慮的因素幸乒,內(nèi)存越多會(huì)被最先kill掉懦底,所以把上面的業(yè)務(wù)邏輯放到Service中,而Service是在另外一個(gè) 進(jìn)程中罕扎,在MainActivity開啟這個(gè)服務(wù)就行了聚唐,這樣這個(gè)進(jìn)程就更加的輕量,

public class LiveService extends Service {

    public  static void toLiveService(Context pContext){
        Intent intent=new Intent(pContext,LiveService.class);
        pContext.startService(intent);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //屏幕關(guān)閉的時(shí)候啟動(dòng)一個(gè)1像素的Activity腔召,開屏的時(shí)候關(guān)閉Activity
        final ScreenManager screenManager = ScreenManager.getInstance(LiveService.this);
        ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
        listener.registerListener(new ScreenBroadcastListener.ScreenStateListener() {
            @Override
            public void onScreenOn() {
                screenManager.finishActivity();
            }
            @Override
            public void onScreenOff() {
                screenManager.startActivity();
            }
        });
        return START_REDELIVER_INTENT;
    }
}

      <service android:name=".LiveService"
            android:process=":live_service"/>

OK杆查,通過(guò)上面的操作,我們的應(yīng)用就始終和前臺(tái)進(jìn)程是一樣的優(yōu)先級(jí)了臀蛛,為了省電亲桦,系統(tǒng)檢測(cè)到鎖屏事件后一段時(shí)間內(nèi)會(huì)殺死后臺(tái)進(jìn)程,如果采取這種方案浊仆,就可以避免了這個(gè)問(wèn)題客峭。但是還是有被殺掉的可能,所以我們還需要做雙進(jìn)程守護(hù)抡柿,關(guān)于雙進(jìn)程守護(hù)舔琅,比較適合的就是aidl的那種方式,但是這個(gè)不是完全的靠譜洲劣,原理是A進(jìn)程死的時(shí)候备蚓,B還在活著,B可以將A進(jìn)程拉起來(lái)闪檬,反之星著,B進(jìn)程死的時(shí)候购笆,A還活著粗悯,A可以將B拉起來(lái)。所以雙進(jìn)程守護(hù)的前提是同欠,系統(tǒng)殺進(jìn)程只能一個(gè)個(gè)的去殺样傍,如果一次性殺兩個(gè),這種方法也是不OK的铺遂。

事實(shí)上
那么我們先來(lái)看看Android5.0以下的源碼衫哥,ActivityManagerService是如何關(guān)閉在應(yīng)用退出后清理內(nèi)存的

Process.killProcessQuiet(pid);  

應(yīng)用退出后,ActivityManagerService就把主進(jìn)程給殺死了襟锐,但是撤逢,在Android5.0以后,ActivityManagerService卻是這樣處理的:

Process.killProcessQuiet(app.pid);  
Process.killProcessGroup(app.info.uid, app.pid);  

在應(yīng)用退出后,ActivityManagerService不僅把主進(jìn)程給殺死蚊荣,另外把主進(jìn)程所屬的進(jìn)程組一并殺死初狰,這樣一來(lái),由于子進(jìn)程和主進(jìn)程在同一進(jìn)程組互例,子進(jìn)程在做的事情奢入,也就停止了。所以在Android5.0以后的手機(jī)應(yīng)用在進(jìn)程被殺死后媳叨,要采用其他方案腥光。

2、前臺(tái)服務(wù)

糊秆,這方案實(shí)際利用了Android前臺(tái)service的漏洞武福。
原理如下
對(duì)于 API level < 18 :調(diào)用startForeground(ID, new Notification())痘番,發(fā)送空的Notification 艘儒,圖標(biāo)則不會(huì)顯示。
對(duì)于 API level >= 18:在需要提優(yōu)先級(jí)的service A啟動(dòng)一個(gè)InnerService夫偶,兩個(gè)服務(wù)同時(shí)startForeground界睁,且綁定同樣的 ID。Stop 掉InnerService 兵拢,這樣通知欄圖標(biāo)即被移除翻斟。

public class KeepLiveService extends Service {

    public static final int NOTIFICATION_ID=0x11;

    public KeepLiveService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
         //API 18以下,直接發(fā)送Notification并將其置為前臺(tái)
        if (Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN_MR2) {
            startForeground(NOTIFICATION_ID, new Notification());
        } else {
            //API 18以上说铃,發(fā)送Notification并將其置為前臺(tái)后访惜,啟動(dòng)InnerService
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            startService(new Intent(this, InnerService.class));
        }
    }

    public  static class  InnerService extends Service{
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            //發(fā)送與KeepLiveService中ID相同的Notification,然后將其取消并取消自己的前臺(tái)顯示
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopForeground(true);
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(NOTIFICATION_ID);
                    stopSelf();
                }
            },100);

        }
    }
}

在沒(méi)有采取前臺(tái)服務(wù)之前腻扇,啟動(dòng)應(yīng)用债热,oom_adj值是0,按下返回鍵之后幼苛,變成9(不同ROM可能不一樣)

Picture

在采取前臺(tái)服務(wù)之后窒篱,啟動(dòng)應(yīng)用,oom_adj值是0舶沿,按下返回鍵之后墙杯,變成2(不同ROM可能不一樣),確實(shí)進(jìn)程的優(yōu)先級(jí)有所提高括荡。

Picture

3高镐、相互喚醒

相互喚醒的意思就是,假如你手機(jī)里裝了支付寶畸冲、淘寶嫉髓、天貓观腊、UC等阿里系的app,那么你打開任意一個(gè)阿里系的app后算行,有可能就順便把其他阿里系的app給喚醒了恕沫。這個(gè)完全有可能的。此外纱意,開機(jī)婶溯,網(wǎng)絡(luò)切換、拍照偷霉、拍視頻時(shí)候迄委,利用系統(tǒng)產(chǎn)生的廣播也能喚醒a(bǔ)pp,不過(guò)Android N已經(jīng)將這三種廣播取消了类少。

Picture
Picture

如果應(yīng)用想毙鹕恚活,要是QQ硫狞,微信愿意救你也行信轿,有多少手機(jī)上沒(méi)有QQ,微信呢残吩?或者像友盟财忽,信鴿這種推送SDK,也存在喚醒a(bǔ)pp的功能泣侮。
拉活方法

4即彪、JobSheduler

JobSheduler是作為進(jìn)程死后復(fù)活的一種手段,native進(jìn)程方式最大缺點(diǎn)是費(fèi)電活尊, Native 進(jìn)程費(fèi)電的原因是感知主進(jìn)程是否存活有兩種實(shí)現(xiàn)方式隶校,在 Native 進(jìn)程中通過(guò)死循環(huán)或定時(shí)器,輪訓(xùn)判斷主進(jìn)程是否存活蛹锰,當(dāng)主進(jìn)程不存活時(shí)進(jìn)行拉活深胳。其次5.0以上系統(tǒng)不支持。 但是JobSheduler可以替代在Android5.0以上native進(jìn)程方式铜犬,這種方式即使用戶強(qiáng)制關(guān)閉舞终,也能被拉起來(lái),親測(cè)可行翎苫。

  JobSheduler@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
    @Override
    public void onCreate() {
        super.onCreate();
        startJobSheduler();
    }

    public void startJobSheduler() {
        try {
            JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()));
            builder.setPeriodic(5);
            builder.setPersisted(true);
            JobScheduler jobScheduler = (JobScheduler) this.getSystemService(Context.JOB_SCHEDULER_SERVICE);
            jobScheduler.schedule(builder.build());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        return false;
    }
}

5权埠、粘性服務(wù)&與系統(tǒng)服務(wù)捆綁

這個(gè)是系統(tǒng)自帶的榨了,onStartCommand方法必須具有一個(gè)整形的返回值煎谍,這個(gè)整形的返回值用來(lái)告訴系統(tǒng)在服務(wù)啟動(dòng)完畢后,如果被Kill龙屉,系統(tǒng)將如何操作呐粘,這種方案雖然可以满俗,但是在某些情況or某些定制ROM上可能失效,我認(rèn)為可以多做一種保保守方案作岖。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_REDELIVER_INTENT;
}

  • START_STICKY
    如果系統(tǒng)在onStartCommand返回后被銷毀唆垃,系統(tǒng)將會(huì)重新創(chuàng)建服務(wù)并依次調(diào)用onCreate和onStartCommand(注意:根據(jù)測(cè)試Android2.3.3以下版本只會(huì)調(diào)用onCreate根本不會(huì)調(diào)用onStartCommand,Android4.0可以辦到)痘儡,這種相當(dāng)于服務(wù)又重新啟動(dòng)恢復(fù)到之前的狀態(tài)了)辕万。

  • START_NOT_STICKY
    如果系統(tǒng)在onStartCommand返回后被銷毀,如果返回該值沉删,則在執(zhí)行完onStartCommand方法后如果Service被殺掉系統(tǒng)將不會(huì)重啟該服務(wù)渐尿。

  • START_REDELIVER_INTENT
    START_STICKY的兼容版本,不同的是其不保證服務(wù)被殺后一定能重啟矾瑰。

的研究砖茸,這里說(shuō)的系統(tǒng)服務(wù)很好理解,比如NotificationListenerService殴穴,NotificationListenerService就是一個(gè)監(jiān)聽通知的服務(wù)凉夯,只要手機(jī)收到了通知,NotificationListenerService都能監(jiān)聽到采幌,即時(shí)用戶把進(jìn)程殺死劲够,也能重啟,所以說(shuō)要是把這個(gè)服務(wù)放到我們的進(jìn)程之中休傍,那么就可以呵呵了

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class LiveService extends NotificationListenerService {

    public LiveService() {

    }

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
    }
}

但是這種方式需要權(quán)限

  <service
            android:name=".LiveService"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>

所以你的應(yīng)用要是有消息推送的話再沧,那么可以用這種方式去欺騙用戶。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尊残,一起剝皮案震驚了整個(gè)濱河市炒瘸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寝衫,老刑警劉巖顷扩,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異慰毅,居然都是意外死亡隘截,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門汹胃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)婶芭,“玉大人,你說(shuō)我怎么就攤上這事着饥∠” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵宰掉,是天一觀的道長(zhǎng)呵哨。 經(jīng)常有香客問(wèn)我赁濒,道長(zhǎng),這世上最難降的妖魔是什么孟害? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任拒炎,我火速辦了婚禮,結(jié)果婚禮上挨务,老公的妹妹穿的比我還像新娘击你。我一直安慰自己,他們只是感情好谎柄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布果漾。 她就那樣靜靜地躺著,像睡著了一般谷誓。 火紅的嫁衣襯著肌膚如雪绒障。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天捍歪,我揣著相機(jī)與錄音户辱,去河邊找鬼。 笑死糙臼,一個(gè)胖子當(dāng)著我的面吹牛庐镐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播变逃,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼必逆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了揽乱?” 一聲冷哼從身側(cè)響起名眉,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凰棉,沒(méi)想到半個(gè)月后损拢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撒犀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年福压,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片或舞。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荆姆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出映凳,到底是詐尸還是另有隱情胆筒,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布魏宽,位于F島的核電站腐泻,受9級(jí)特大地震影響决乎,放射性物質(zhì)發(fā)生泄漏队询。R本人自食惡果不足惜派桩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚌斩。 院中可真熱鬧铆惑,春花似錦、人聲如沸送膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叠聋。三九已至撕阎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碌补,已是汗流浹背虏束。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厦章,地道東北人镇匀。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袜啃,于是被迫代替她去往敵國(guó)和親汗侵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 自己曾經(jīng)也在這個(gè)問(wèn)題上傷過(guò)腦經(jīng),前幾日剛好有一個(gè)北京的哥們?cè)赒Q說(shuō)在做IM類的項(xiàng)目熟妓,問(wèn)我進(jìn)程惫溃活如何處理比較恰當(dāng),...
    4e70992f13e7閱讀 3,767評(píng)論 9 158
  • 目的 進(jìn)程保活是各個(gè)公司想要實(shí)現(xiàn)的一個(gè)功能告材,可以“無(wú)賴”地一直霸占你的手機(jī)坤次,不被系統(tǒng)和第三方殺掉,以下是幾種方案斥赋,...
    Eren丶耶格爾閱讀 728評(píng)論 0 21
  • 如何進(jìn)行進(jìn)程卑探#活滑绒,首先我們應(yīng)該先分析一下進(jìn)程被殺死的原因開始 Android進(jìn)程被殺死的場(chǎng)景分析: 從 Andro...
    如穎隨行日記閱讀 4,895評(píng)論 2 4
  • 如何進(jìn)行進(jìn)程币晒剩活杠览,首先我們應(yīng)該先分析一下進(jìn)程被殺死的原因開始 Android進(jìn)程被殺死的場(chǎng)景分析: 從 Andro...
    編程小豬閱讀 3,496評(píng)論 1 14
  • ubuntu: 1、在普通用戶下 ps -aux | grep tomcat 然后 sudo kill -9 殺...
    小銘銘_7c47閱讀 238評(píng)論 0 0