Service進(jìn)程防殺

什么是進(jìn)程

進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。

進(jìn)程的概念主要有兩點(diǎn):第一,進(jìn)程是一個(gè)實(shí)體。每一個(gè)進(jìn)程都有它自己的地址空間,一般情況下,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)。文本區(qū)域存儲(chǔ)處理器執(zhí)行的代碼;數(shù)據(jù)區(qū)域存儲(chǔ)變量和進(jìn)程執(zhí)行期間使用的動(dòng)態(tài)分配的內(nèi)存;堆棧區(qū)域存儲(chǔ)著活動(dòng)過程調(diào)用的指令和本地變量。第二,進(jìn)程是一個(gè)“執(zhí)行中的程序”。程序是一個(gè)沒有生命的實(shí)體,只有處理器賦予程序生命時(shí)(操作系統(tǒng)執(zhí)行之),它才能成為一個(gè)活動(dòng)的實(shí)體,我們稱其為進(jìn)程。

Android Service

Service是Android系統(tǒng)中的四大組件之一,它是一種長生命周期的,沒有可視化界面運(yùn)行于后臺(tái)的一種服務(wù)程序。

當(dāng)一個(gè)程序第一次啟動(dòng)的時(shí)候,Android會(huì)啟動(dòng)一個(gè)LINUX進(jìn)程和一個(gè)主線程。默認(rèn)的情況下,所有該程序的組件都將在該進(jìn)程和線程中運(yùn)行。 同時(shí),Android會(huì)為每個(gè)應(yīng)用程序分配一個(gè)單獨(dú)的LINUX用戶。Android會(huì)盡量保留一個(gè)正在運(yùn)行進(jìn)程,只在內(nèi)存資源出現(xiàn)不足時(shí),Android會(huì)嘗試停止一些進(jìn)程從而釋放足夠的資源給其他新的進(jìn)程使用, 也能保證用戶正在訪問的當(dāng)前進(jìn)程有足夠的資源去及時(shí)地響應(yīng)用戶的事件。

我們可以將一些組件運(yùn)行在其他進(jìn)程中,并且可以為任意的進(jìn)程添加線程。組件運(yùn)行在哪個(gè)進(jìn)程中是在manifest文件里設(shè)置的,其中<Activity>,<Service>,<receiver>和<provider>都有一個(gè)process屬性來指定該組件運(yùn)行在哪個(gè)進(jìn)程之中。我們可以設(shè)置這個(gè)屬性,使得每個(gè)組件運(yùn)行在它們自己的進(jìn)程中,或是幾個(gè)組件共同享用一個(gè)進(jìn)程,或是不共同享用。<application>元素也有一個(gè)process屬性,用來指定所有的組件的默認(rèn)屬性。

Service的主要作用:

  1. 后臺(tái)運(yùn)行
    ?Service沒有自己的界面,用戶察覺不到,看似在后臺(tái)運(yùn)行。默認(rèn)情況下Service是運(yùn)行在主線程中的,為了不造成主線程阻塞,所以在Service中要處理耗時(shí)操作需要放到其他線程中去處理。
  2. 跨進(jìn)程通信
    ? 由于進(jìn)程與進(jìn)程之間是獨(dú)立分開的,一個(gè)進(jìn)程不能訪問另外一個(gè)進(jìn)程的資源。Android提供了一種跨進(jìn)程通信的方式AIDL。這種方式通過一個(gè)進(jìn)程綁定另一個(gè)進(jìn)程的Binder Service來實(shí)現(xiàn)。

進(jìn)程優(yōu)先級(jí)

Android系統(tǒng)會(huì)盡可能長的延續(xù)一個(gè)應(yīng)用程序進(jìn)程,但在內(nèi)存過低的時(shí)候,仍然會(huì)不可避免需要移除舊的進(jìn)程。為了決定哪些進(jìn)程留下,哪些進(jìn)程被殺死,系統(tǒng)根據(jù)在進(jìn)程中在運(yùn)行的組件及組件的狀態(tài),為每一個(gè)進(jìn)程分配了一個(gè)優(yōu)先級(jí)等級(jí)。優(yōu)先級(jí)最低的進(jìn)程首先被殺死。這個(gè)進(jìn)程重要性的層次結(jié)構(gòu)主要有五個(gè)等級(jí):

  • 前臺(tái)進(jìn)程
  • 可見進(jìn)程
  • 服務(wù)進(jìn)程
  • 后臺(tái)進(jìn)程
  • 空進(jìn)程

前臺(tái)進(jìn)程

前臺(tái)進(jìn)程是用戶當(dāng)前正在使用的進(jìn)程。只有一些前臺(tái)進(jìn)程可以在任何時(shí)候都存在。他們是最后一個(gè)被結(jié)束的,當(dāng)內(nèi)存低到根本連他們都不能運(yùn)行的時(shí)候。一般來說, 在這種情況下,設(shè)備會(huì)進(jìn)行內(nèi)存調(diào)度,中止一些前臺(tái)進(jìn)程來保持對(duì)用戶交互的響應(yīng)。因?yàn)闅⑺狼芭_(tái)進(jìn)行需要用戶交互,所以前臺(tái)進(jìn)程優(yōu)先級(jí)是最高的。

前臺(tái)進(jìn)程常見情形:

  • 進(jìn)程持有一個(gè)正在與用戶交互的Activity(這個(gè)Activity的onResume方法被調(diào)用)
  • 進(jìn)程持有一個(gè)Service,這個(gè)Service處于以下幾種狀態(tài)
    • Service與用戶正在交互的Activity綁定
    • Service是在前臺(tái)運(yùn)行的(其調(diào)用了startForeground())
    • Service正在執(zhí)行其生命周期回調(diào)方法(onCreate(),onStart(),onDestroy())
  • 進(jìn)程持有一個(gè)BroadcastReceiver正在執(zhí)行其onReceive方法

可見進(jìn)程

如果一個(gè)進(jìn)程不含有任何前臺(tái)的組件,但仍然可被用戶再屏幕上所見??梢娺M(jìn)程也被認(rèn)為很重要的,一般不會(huì)被銷毀,除非是為了保證所有前臺(tái)進(jìn)程的運(yùn)行而不得不殺死可見進(jìn)程的時(shí)候。

可見進(jìn)程常見情形:

  • 進(jìn)程持有一個(gè)Activity,這個(gè)Activity不在前臺(tái),但是仍然被用戶可見(onPause)

  • 進(jìn)程持有一個(gè)Service,這個(gè)Service和一個(gè)可見的(或前臺(tái)的)Activity綁定。

服務(wù)進(jìn)程

如果一個(gè)進(jìn)程中運(yùn)行著一個(gè)Service,這個(gè)Service是通過startService()開啟的,并且不屬于上面兩種較高優(yōu)先級(jí)的情況,這個(gè)進(jìn)程就是一個(gè)服務(wù)進(jìn)程。

盡管服務(wù)進(jìn)程沒有和用戶可以看到的東西綁定,但是它們一般在做的事情是用戶關(guān)心的,比如后臺(tái)播放音樂,后臺(tái)下載數(shù)據(jù)等。所以系統(tǒng)會(huì)盡量維持它們的運(yùn)行,除非系統(tǒng)內(nèi)存不足以維持前臺(tái)進(jìn)程和可見進(jìn)程的運(yùn)行需要。

后臺(tái)進(jìn)程

如果進(jìn)程不屬于上面三種情況,但是進(jìn)程持有一個(gè)用戶不可見的activity(activity的onStop()被調(diào)用,但是onDestroy()沒有調(diào)用的狀態(tài)),就認(rèn)為進(jìn)程是一個(gè)后臺(tái)進(jìn)程。

后臺(tái)進(jìn)程不直接影響用戶體驗(yàn),系統(tǒng)會(huì)為了前臺(tái)進(jìn)程、可見進(jìn)程、服務(wù)進(jìn)程而任意殺死后臺(tái)進(jìn)程。

通常會(huì)有很多個(gè)后臺(tái)進(jìn)程存在,它們會(huì)被保存在一個(gè)LRU (least recently used)列表中,這樣就可以確保用戶最近使用的activity最后被銷毀,即最先銷毀時(shí)間最遠(yuǎn)的activity。

空進(jìn)程

如果一個(gè)進(jìn)程不包含任何活躍的應(yīng)用組件,則認(rèn)為是空進(jìn)程。
例如:一個(gè)進(jìn)程當(dāng)中已經(jīng)沒有數(shù)據(jù)在運(yùn)行了,但是內(nèi)存當(dāng)中還為這個(gè)應(yīng)用駐留了一個(gè)進(jìn)程空間。
保存這種進(jìn)程的唯一理由是為了緩存的需要,為了加快下次要啟動(dòng)這個(gè)進(jìn)程中的組件時(shí)的啟動(dòng)時(shí)間。系統(tǒng)為了平衡進(jìn)程緩存和底層內(nèi)核緩存的資源,經(jīng)常會(huì)殺死空進(jìn)程。

使用進(jìn)程的優(yōu)先級(jí)

Android 對(duì)進(jìn)程的重要性評(píng)級(jí)的時(shí)候,選取它最高的級(jí)別。例如,如果一個(gè)進(jìn)程含有一個(gè)service和一個(gè)可視activity,進(jìn)程將被歸入一個(gè)可視進(jìn)程而不是service進(jìn)程。

另外,當(dāng)被某個(gè)進(jìn)程被另外的一個(gè)進(jìn)程依賴的時(shí)候,它的級(jí)別可能會(huì)增高。也就意味著一個(gè)為其他進(jìn)程服務(wù)的進(jìn)程永遠(yuǎn)會(huì)比那些被服務(wù)的進(jìn)程重要級(jí)高。

因?yàn)?strong>服務(wù)進(jìn)程比后臺(tái)進(jìn)程重要級(jí)高,因此一個(gè)要進(jìn)行耗時(shí)工作的Activity最好啟動(dòng)一個(gè)Service來做這個(gè)工作,而不是在Activity中開啟一個(gè)子線程――特別是這個(gè)操作需要的時(shí)間比Activity存在的時(shí)間還要長的時(shí)候。例如,在后臺(tái)播放音樂,向網(wǎng)上上傳攝像頭拍到的圖片,使用Service可以讓其進(jìn)程至少獲取到服務(wù)進(jìn)程級(jí)別的重要級(jí),而不用考慮Activity目前是什么狀態(tài)。BroadcastReceiver做費(fèi)時(shí)的工作的時(shí)候,也應(yīng)該啟用一個(gè)服務(wù)而不是開一個(gè)線程。

進(jìn)程防殺

提高進(jìn)程優(yōu)先級(jí)

提升進(jìn)程優(yōu)先級(jí)至當(dāng)前進(jìn)程

之前小米工程師在微博上爆出QQ為了?;?,采取在鎖屏的時(shí)候啟動(dòng)一個(gè)1個(gè)像素的Activity,當(dāng)用戶解鎖以后將這個(gè)Activity結(jié)束掉(順便同時(shí)把自己的核心服務(wù)再開啟一次)。這種做法很不容易被用戶發(fā)現(xiàn)。

修改Manifest android:priority

在AndroidManifest.xml文件中對(duì)于intent-filter可以通過android:priority = “1000”這個(gè)屬性設(shè)置最高優(yōu)先級(jí),1000是最高值,如果數(shù)字越小則優(yōu)先級(jí)越低,同時(shí)適用于廣播。

Service提升為前臺(tái)進(jìn)程

在onStartCommand里面調(diào)用 startForeground()方法把Service提升為前臺(tái)進(jìn)程級(jí)別,然后在onDestroy里面要記得調(diào)用stopForeground ()方法。

Service onStartCommand 返回 START_STICKY。

在Service的onStartCommand方法中手動(dòng)返回START_STICKY,親測當(dāng)service因內(nèi)存不足被kill,當(dāng)內(nèi)存又有的時(shí)候,service又被重新創(chuàng)建,比較不錯(cuò),但是不能保證任何情況下都被重建,比如進(jìn)程被干掉了。

補(bǔ)充說明:onStartCommand()方法,返回的是一個(gè)int整形。
這個(gè)整形可以有以下四個(gè)取值:

  1. START_STICKY:如果service進(jìn)程被kill掉,保留service的狀態(tài)為開始狀態(tài),但不保留遞送的intent對(duì)象。隨后系統(tǒng)會(huì)嘗試重新創(chuàng)建service,由于服務(wù)狀態(tài)為開始狀態(tài),所以創(chuàng)建服務(wù)后一定會(huì)調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動(dòng)命令被傳遞到service,那么參數(shù)Intent將為null。
  2. START_NOT_STICKY:“非粘性的”。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand后,服務(wù)被異常kill掉,系統(tǒng)不會(huì)自動(dòng)重啟該服務(wù)
  3. START_REDELIVER_INTENT:重傳Intent。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand后,服務(wù)被異常kill掉,系統(tǒng)會(huì)自動(dòng)重啟該服務(wù),并將Intent的值傳入。
  4. START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務(wù)被kill后一定能重啟。

在onDestroy方法里發(fā)廣播重啟service

service + broadcast 方式,就是當(dāng)service走ondestory的時(shí)候,發(fā)送一個(gè)自定義的廣播,當(dāng)收到廣播的時(shí)候,重新啟動(dòng)service。(第三方應(yīng)用或是在setting里-應(yīng)用-強(qiáng)制停止時(shí),APP進(jìn)程就直接被干掉了,onDestroy方法都進(jìn)不來,所以無法保證會(huì)執(zhí)行)

監(jiān)聽系統(tǒng)廣播判斷Service狀態(tài)

通過系統(tǒng)的一些廣播,比如:手機(jī)重啟、界面喚醒、應(yīng)用狀態(tài)改變等等監(jiān)聽并捕獲到,然后判斷我們的Service是否還存活,如果我們的Service不存活時(shí)就啟動(dòng)起來。

Application加上Persistent屬性

看Android的文檔知道,當(dāng)進(jìn)程長期不活動(dòng),或系統(tǒng)需要資源時(shí),會(huì)自動(dòng)清理門戶,殺死一些Service,和不可見的Activity等所在的進(jìn)程。但是如果某個(gè)進(jìn)程不想被殺死(如數(shù)據(jù)緩存進(jìn)程,或狀態(tài)監(jiān)控進(jìn)程,或遠(yuǎn)程服務(wù)進(jìn)程),應(yīng)該怎么做,才能使進(jìn)程不被殺死。

add android:persistent=”true” into the section in your AndroidManifest.xml

加上以上屬性相當(dāng)于將該進(jìn)程設(shè)置為常駐內(nèi)存進(jìn)程。
切記,這個(gè)不可濫用,一般只適用于放在/system/app下的app,系統(tǒng)中用這個(gè)的service,app一多,整個(gè)系統(tǒng)可能就會(huì)崩潰。
比如系統(tǒng)phone中配置了android:persistent=”true”屬性,并且Phone.apk是安裝在/system/app/目錄下的,所以在開機(jī)時(shí)會(huì)自動(dòng)啟動(dòng)PhoneApp類。

手機(jī)廠商白名單

app運(yùn)營商和某些手機(jī)廠商可能有合作關(guān)系,讓自家的進(jìn)程怎么殺也殺不死,讓自家的應(yīng)用卸載不了。

雙進(jìn)程守護(hù)

上述防進(jìn)程被殺的方法,也并不能完全保證進(jìn)程不被系統(tǒng)殺死。大多做法都能被用戶在Setting設(shè)置里給停掉,或者被用戶安裝的清理軟件給殺掉。

那么怎么才能讓我們的進(jìn)程被殺掉后再活過來呢?想必大家都聽說過雙進(jìn)程守護(hù)的方式吧。具體實(shí)現(xiàn)原理是使用兩個(gè)進(jìn)程來相互監(jiān)聽存活狀態(tài),相互啟動(dòng)。為何要使用兩個(gè)進(jìn)程呢?

我們都知道在Android中讓兩個(gè)進(jìn)程相互通信使用的是AIDL技術(shù)。所以我們需要用到Service的遠(yuǎn)程綁定,讓每個(gè)進(jìn)程中都啟動(dòng)一個(gè)Service,啟動(dòng)之后再與另一個(gè)進(jìn)程的Service進(jìn)行綁定,綁定成功之后監(jiān)聽對(duì)面Service的狀態(tài)。如果其中一個(gè)Service被干掉了,另一個(gè)Service會(huì)再通過AIDL把被干掉的Service啟動(dòng)起來。

以下是具體實(shí)現(xiàn)的一個(gè)例子,在同一個(gè)程序內(nèi)開啟了兩個(gè)服務(wù),LocalService,RemoteService。這兩個(gè)服務(wù)分別屬于不同進(jìn)程。

項(xiàng)目結(jié)構(gòu):


這里寫圖片描述

首先定義一個(gè)AIDL文件:

// IKeepLiveConnection.aidl
package com.congwiny.keepliveprocess;

// Declare any non-default types here with import statements

interface IKeepLiveConnection {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.congwiny.keepliveprocess">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".LocalService"/>

        <service
            android:name=".RemoteService"
            android:process=":remote" />
    </application>

</manifest>

MainActivity

package com.congwiny.keepliveprocess;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        startService(new Intent(this,LocalService.class));
        startService(new Intent(this,RemoteService.class));
    }
}

LocalService

package com.congwiny.keepliveprocess;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class LocalService extends Service {

    private static final String TAG = LocalService.class.getSimpleName();

    private KeepLiveBinder binder;
    private KeepLiveServiceConnection conn;

    @Override
    public void onCreate() {
        super.onCreate();
        binder = new KeepLiveBinder();
        conn = new KeepLiveServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Intent intentNew = new Intent();
        intentNew.setClass(this, RemoteService.class);
        bindService(intentNew, conn, BIND_IMPORTANT);
        return START_STICKY;
    }

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

    private class KeepLiveServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "remote service onServiceConnected");

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "remote service onServiceDisconnected");
            //重新綁定RemoteService
            Intent intent = new Intent();
            intent.setClass(LocalService.this, RemoteService.class);
            startService(intent);
            bindService(intent, conn, BIND_IMPORTANT);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }

    private class KeepLiveBinder extends IKeepLiveConnection.Stub {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    }
}

RemoteService

package com.congwiny.keepliveprocess;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class RemoteService extends Service {

    private static final String TAG = RemoteService.class.getSimpleName();
    private KeepLiveBinder binder;
    private KeepLiveServiceConnection conn;

    @Override
    public void onCreate() {
        super.onCreate();
        binder = new KeepLiveBinder();
        conn = new KeepLiveServiceConnection();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Intent intentNew = new Intent();
        intentNew.setClass(this, LocalService.class);
        bindService(intentNew, conn, BIND_IMPORTANT);

        return START_STICKY;
    }

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

    private class KeepLiveServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "local service onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "local service onServiceDisconnected");

            Intent intent = new Intent();
            intent.setClass(RemoteService.this, LocalService.class);
            //LocalService已經(jīng)掛了,先必須先啟動(dòng),后綁定
            startService(intent);
            bindService(intent, conn, BIND_IMPORTANT);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //解綁,否則會(huì)出現(xiàn) leaked ServiceConnection
        unbindService(conn);
    }

    private class KeepLiveBinder extends IKeepLiveConnection.Stub {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    }
}

運(yùn)行效果圖:


這里寫圖片描述

JobScheduler

JobScheduler是Android5.0新增的API,JobScheduler API允許開發(fā)者在符合某些條件時(shí)創(chuàng)建執(zhí)行在后臺(tái)的任務(wù)。具體細(xì)節(jié)這里就不做描述。參考
在Android 5.0中使用JobScheduler

我們?yōu)槭裁匆褂眠@個(gè)API呢?

當(dāng)我們手動(dòng)點(diǎn)擊找到該應(yīng)用信息頁面,有個(gè)結(jié)束運(yùn)行或者強(qiáng)行停止按鈕,如果我們點(diǎn)擊這個(gè)按鈕后,我們的雙進(jìn)程守護(hù)也不管用了,我們的程序的進(jìn)程會(huì)被強(qiáng)制殺死,再也啟動(dòng)不起來。

這里寫圖片描述

通過實(shí)際操作不同的手機(jī),有的手機(jī)中運(yùn)行的進(jìn)程被殺死后,能在使用JobScheduler的情況下啟動(dòng),而有些手機(jī)是不可以的。
代碼實(shí)現(xiàn):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.congwiny.keepliveprocess">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".LocalService"/>

        <service
            android:name=".RemoteService"
            android:process=":remote" />

        <service android:name=".JobSchedulerService"
            android:permission="android.permission.BIND_JOB_SERVICE"/>
    </application>

</manifest>
package com.congwiny.keepliveprocess;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        startService(new Intent(this,LocalService.class));
        startService(new Intent(this,RemoteService.class));
        startService(new Intent(this, JobSchedulerService.class));
    }
}
package com.congwiny.keepliveprocess;

import java.util.List;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

@SuppressLint("NewApi")
public class JobSchedulerService extends JobService {

    private static final String TAG = JobSchedulerService.class.getSimpleName();

    private int kJobId = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        scheduleJob(getJobInfo());
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.e(TAG, "onStartJob");
        boolean isLocalServiceWork = isServiceWork(this, "com.congwiny.keepliveprocess.LocalService");
        boolean isRemoteServiceWork = isServiceWork(this, "com.congwiny.keepliveprocess.RemoteService");

        if (!isLocalServiceWork ||
                !isRemoteServiceWork) {
            this.startService(new Intent(this, LocalService.class));
            this.startService(new Intent(this, RemoteService.class));
            Toast.makeText(this, "job scheduler service start", Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.e(TAG, "onStopJob");
        Toast.makeText(this, "job scheduler service stop", Toast.LENGTH_SHORT).show();
        scheduleJob(getJobInfo());
        return true;
    }

    /**
     * Send job to the JobScheduler.
     */
    public void scheduleJob(JobInfo t) {
        JobScheduler tm =
                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        tm.schedule(t);
    }

    public JobInfo getJobInfo() {
        JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, JobSchedulerService.class));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setPersisted(true);
        builder.setRequiresCharging(false);
        builder.setRequiresDeviceIdle(false);
        builder.setPeriodic(1000);//間隔時(shí)間--周期
        return builder.build();
    }


    /**
     * 判斷某個(gè)服務(wù)是否正在運(yùn)行的方法
     *
     * @return true代表正在運(yùn)行,false代表服務(wù)沒有正在運(yùn)行
     */
    public boolean isServiceWork(Context mContext, String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) mContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> myList = myAM.getRunningServices(100);
        if (myList.size() <= 0) {
            return false;
        }
        for (int i = 0; i < myList.size(); i++) {
            String mName = myList.get(i).service.getClassName().toString();
            if (mName.equals(serviceName)) {
                isWork = true;
                break;
            }
        }
        return isWork;
    }
}

擴(kuò)展

Service和Thread的關(guān)系

Android中Service和Thread有什么區(qū)別,為什么有時(shí)候放著方便的Thread不用,而要使用Service呢?

要解答這個(gè)問題,首先我們要弄明白Service和Thread分別是怎么定義的:

  • Thread 是程序執(zhí)行的最小單元,它是分配CPU的基本單位??梢杂?Thread 來執(zhí)行一些異步的操作。

  • Service是Android的四大組件之一,被用來執(zhí)行長時(shí)間的后臺(tái)任務(wù)。默認(rèn)情況下Service是運(yùn)行在主線程中的。

二者的使用上的區(qū)別

  1. 在android中,Thread只是一個(gè)用來執(zhí)行后臺(tái)任務(wù)的工具類,它可以在Activity中被創(chuàng)建,也可以在Service中被創(chuàng)建。

  2. Service組件主要有兩個(gè)作用:后臺(tái)運(yùn)行和跨進(jìn)程訪問。service可以在android系統(tǒng)后臺(tái)獨(dú)立運(yùn)行并且可以跨進(jìn)程運(yùn)行,線程是不可以。

  3. 如果需要執(zhí)行復(fù)雜耗時(shí)的操作,必須在Service中再創(chuàng)建一個(gè)Thread來執(zhí)行任務(wù)。Service的優(yōu)先級(jí)高于后臺(tái)掛起的Activity,當(dāng)然也高于Activity所創(chuàng)建的Thread,因此,系統(tǒng)可能在內(nèi)存不足的時(shí)候優(yōu)先殺死后臺(tái)的Activity或者Thread,而不會(huì)輕易殺死Service組件,即使被迫殺死Service,也會(huì)在資源可用時(shí)重啟被殺死的Service。

Activity和Service是否處于同一進(jìn)程

默認(rèn)同一個(gè)應(yīng)用程序的Activity和Service都屬于同一個(gè)進(jìn)程,Activity和Service都運(yùn)行在主線程里。但如果在AndroidManifest.xml中給Service設(shè)置android:process="xxx",此Service就是屬于"xxx"進(jìn)程了。

總結(jié)

上述進(jìn)程防殺給出一些思路,由于Android手機(jī)定制機(jī)型太多,有些底層已經(jīng)被改,所以上述方法并不能保證在每個(gè)手機(jī)上都行的通,只是做個(gè)參考而已。進(jìn)程防殺這個(gè)話題,引出的知識(shí)點(diǎn)還是很多,以前沒有好好總結(jié)過,不成系統(tǒng),現(xiàn)在總結(jié)出來,擴(kuò)充自己的系統(tǒng),以備自己以后復(fù)習(xí)使用。下面參考的一些文章有時(shí)間的話就學(xué)習(xí)下。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,036評(píng)論 25 709
  • Service service:是一個(gè)后臺(tái)服務(wù),專門用來處理常駐后臺(tái)的工作的組件。 即時(shí)通訊:service來做常...
    出云月閱讀 577評(píng)論 0 0
  • 如何能讓我們的應(yīng)用能夠在系統(tǒng)后臺(tái)持續(xù)地運(yùn)行是一個(gè)自Android從娘胎里出來時(shí)就議論不停的話題,而且這似乎成了一個(gè)...
    駿駿的簡書閱讀 1,184評(píng)論 1 19
  • 勃起的山脈 鎮(zhèn)壓著村落對(duì)自由的向往 風(fēng)在此折翼 海沫如血般潑濺在彼岸 那凋零的意志之花的臉龐 罪惡的利劍 割斷了信...
    死亡詩約閱讀 804評(píng)論 4 3
  • 太陽已經(jīng)升到了半空中,撫摸著正懶洋洋的坐在潤溪湖畔的宿舍樓。上午九時(shí),樓棟中空空如也。小江緩緩?fù)崎_被子,在床上睡眼...
    北方淡季閱讀 508評(píng)論 2 4

友情鏈接更多精彩內(nèi)容