Android進程蹦焉螅活方案總結(jié)

Android進程保活主要包括兩個方面:

  1. 提高進程的優(yōu)先級捌归,降低被殺死的概率
  2. 在進程被殺死后拉活

1.進程優(yōu)先級

Android 系統(tǒng)將盡量長時間地保持應用進程肛响,但為了新建進程或運行更重要的進程,最終需要移除舊進程來回收內(nèi)存惜索。 為了確定保留或終止哪些進程特笋,系統(tǒng)會根據(jù)進程中正在運行的組件以及這些組件的狀態(tài),將每個進程放入“重要性層次結(jié)構(gòu)”中巾兆。 必要時猎物,系統(tǒng)會首先消除重要性最低的進程,然后是重要性略低的進程臼寄,依此類推霸奕,以回收系統(tǒng)資源溜宽。

重要性層次結(jié)構(gòu)一共有 5 級吉拳。以下列表按照重要程度列出了各類進程(第一個進程最重要,將是最后一個被終止的進程):

1.1 前臺進程

用戶當前操作所必需的進程适揉。如果一個進程滿足以下任一條件留攒,即視為前臺進程:

  • 托管用戶正在交互的 Activity(已調(diào)用 Activity 的 onResume() 方法)
  • 托管某個 Service,后者綁定到用戶正在交互的 Activity
  • 托管正在“前臺”運行的 Service(服務已調(diào)用 startForeground())
  • 托管正執(zhí)行一個生命周期回調(diào)的 Service(onCreate()嫉嘀、onStart() 或 onDestroy())
  • 托管正執(zhí)行其 onReceive() 方法的 BroadcastReceiver

通常炼邀,在任意給定時間前臺進程都為數(shù)不多。只有在內(nèi)存不足以支持它們同時繼續(xù)運行這一萬不得已的情況下剪侮,系統(tǒng)才會終止它們拭宁。 此時洛退,設備往往已達到內(nèi)存分頁狀態(tài),因此需要終止一些前臺進程來確保用戶界面正常響應杰标。

1.2 可見進程

沒有任何前臺組件兵怯、但仍會影響用戶在屏幕上所見內(nèi)容的進程。 如果一個進程滿足以下任一條件腔剂,即視為可見進程:

  • 托管不在前臺媒区、但仍對用戶可見的 Activity(已調(diào)用其 onPause() 方法)。例如掸犬,如果前臺 Activity 啟動了一個對話框袜漩,允許在其后顯示上一 Activity,則有可能會發(fā)生這種情況湾碎。
  • 托管綁定到可見(或前臺)Activity 的 Service宙攻。

可見進程被視為是極其重要的進程,除非為了維持所有前臺進程同時運行而必須終止介褥,否則系統(tǒng)不會終止這些進程粘优。

1.3 服務進程

  • 正在運行已使用 startService() 方法啟動的服務且不屬于上述兩個更高類別進程的進程。

盡管服務進程與用戶所見內(nèi)容沒有直接關聯(lián)呻顽,但是它們通常在執(zhí)行一些用戶關心的操作(例如雹顺,在后臺播放音樂或從網(wǎng)絡下載數(shù)據(jù))。因此廊遍,除非內(nèi)存不足以維持所有前臺進程和可見進程同時運行嬉愧,否則系統(tǒng)會讓服務進程保持運行狀態(tài)。

1.4 后臺進程

  • 包含目前對用戶不可見的 Activity 的進程(已調(diào)用 Activity 的 onStop() 方法)喉前。

這些進程對用戶體驗沒有直接影響没酣,系統(tǒng)可能隨時終止它們,以回收內(nèi)存供前臺進程卵迂、可見進程或服務進程使用裕便。 通常會有很多后臺進程在運行,因此它們會保存在 LRU (最近最少使用)列表中见咒,以確保包含用戶最近查看的 Activity 的進程最后一個被終止偿衰。如果某個 Activity 正確實現(xiàn)了生命周期方法,并保存了其當前狀態(tài)改览,則終止其進程不會對用戶體驗產(chǎn)生明顯影響下翎,因為當用戶導航回該 Activity 時,Activity 會恢復其所有可見狀態(tài)宝当。 有關保存和恢復狀態(tài)的信息视事,請參閱 Activity文檔。

1.5 空進程

  • 不含任何活動應用組件的進程庆揩。

保留這種進程的的唯一目的是用作緩存俐东,以縮短下次在其中運行組件所需的啟動時間跌穗。 為使總體系統(tǒng)資源在進程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會終止這些進程虏辫。


2.Android進程回收策略(LowMemoryKiller)

為什么引入LowMemoryKiller瞻离?

進程的啟動分冷啟動和熱啟動,當用戶退出某一個進程的時候乒裆,并不會真正的將進程退出套利,而是將這個進程放到后臺,以便下次啟動的時候可以馬上啟動起來鹤耍,這個過程名為熱啟動肉迫,這也是Android的設計理念之一。這個機制會帶來一個問題稿黄,每個進程都有自己獨立的內(nèi)存地址空間喊衫,隨著應用打開數(shù)量的增多,系統(tǒng)已使用的內(nèi)存越來越大,就很有可能導致系統(tǒng)內(nèi)存不足杆怕。為了解決這個問題族购,系統(tǒng)引入LowmemoryKiller(簡稱lmk)管理所有進程,根據(jù)一定策略來kill某個進程并釋放占用的內(nèi)存陵珍,保證系統(tǒng)的正常運行寝杖。

LowMemoryKiller的基本原理

所有應用進程都是從zygote孵化出來的,記錄在AMS中mLruProcesses列表中互纯,由AMS進行統(tǒng)一管理瑟幕,AMS中會根據(jù)進程的狀態(tài)更新進程對應的oom_adj值,這個值會通過文件傳遞到kernel中去留潦,kernel有個低內(nèi)存回收機制只盹,在內(nèi)存達到一定閥值時會觸發(fā)清理oom_adj值高的進程騰出更多的內(nèi)存空間。

什么是oom_adj兔院?

它是linux內(nèi)核分配給每個系統(tǒng)進程的一個值殖卑,代表進程的優(yōu)先級,進程回收機制就是根據(jù)這個優(yōu)先級來決定是否進行回收坊萝。對于oom_adj的作用孵稽,你只需要記住以下幾點即可:

  • 進程的oom_adj越大,表示此進程優(yōu)先級越低屹堰,越容易被殺回收肛冶;越小街氢,表示進程優(yōu)先級越高扯键,越不容易被殺回收
  • 普通app進程的oom_adj>=0,系統(tǒng)進程的oom_adj才可能<0

minfree 閾值

存放6個數(shù)值,每個數(shù)表示內(nèi)存頁面數(shù),單位是page(一個頁面4kb)

查看方式:

adb shell
su
cat /sys/module/lowmemorykiller/parameters/minfree

得到的數(shù)值為:18432,23040,27648,32256,36864,46080這6個數(shù)值分別代表android系統(tǒng)回收6種進程的閾值珊肃,這么看不方便查看荣刑,轉(zhuǎn)換為M會更直觀馅笙,這6個數(shù)值的單位為page 1page = 4K,所以通過 數(shù)值*4/1024就能轉(zhuǎn)換為M:72M,90M,108M,126M,144M,180M,也就是說1.前臺進程(foreground)厉亏,2.可見進程(visible)董习,3.次要服務(secondary server),4.后臺進程(hidden)爱只,5.內(nèi)容供應節(jié)點(content provider)皿淋,6.空進程(empty)這6類進程進行回收的內(nèi)存閾值分別為72M,90M,108M,126M,144M,180M

oom_adj值

oom_adj的值越小,進程的優(yōu)先級越高

獲取進程oom_adj方式:

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

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

內(nèi)存閾值在不同的手機上不一樣,一旦低于該值,Android便開始按順序關閉進程. 因此Android開始結(jié)束優(yōu)先級最低的空進程幻馁,即當可用內(nèi)存小于180MB(46080)


3.提高進程優(yōu)先級方案

3.1 Activity提權(quán)(降低oom_adj)

原理:

  • 監(jiān)控手機鎖屏解鎖事件洗鸵,在屏幕鎖屏時啟動1個像素透明的 Activity,在用戶解鎖時將 Activity 銷毀掉仗嗦,從而達到提高進程優(yōu)先級的作用膘滨,可以使進程的優(yōu)先級在屏幕鎖屏時間由4提升為最高優(yōu)先級1。

代碼實現(xiàn):

public class KeepManager {
    private static final KeepManager ourInstance = new KeepManager();

    public static KeepManager getInstance() {
        return ourInstance;
    }

    private KeepManager() {
    }
    private KeepReceiver keepReceiver;
    private WeakReference<Activity> mKeepActivity;
    /**
     * 注冊
     * @param context
     */
    public void registerKeepReceiver(Context context){
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        keepReceiver = new KeepReceiver();
        context.registerReceiver(keepReceiver, filter);
    }

    /**
     * 反注冊
     * @param context
     */
    public void unRegisterKeepReceiver(Context context){
        if (null != keepReceiver) {
            context.unregisterReceiver(keepReceiver);
        }
    }

    /**
     * 啟動1個像素的KeepActivity
     * @param context
     */
    public void startKeep(Context context) {
        Intent intent = new Intent(context, KeepActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    /**
     * finish1個像素的KeepActivity
     */
    public void finishKeep() {
        if (null != mKeepActivity) {
            Activity activity = mKeepActivity.get();
            if (null != activity) {
                activity.finish();
            }
            mKeepActivity = null;
        }
    }

    public void setKeepActivity(KeepActivity mKeepActivity) {
        this.mKeepActivity = new WeakReference<Activity>(mKeepActivity);
    }
}
public class KeepReceiver extends BroadcastReceiver {
    private static final String TAG = "KeepReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.e(TAG, "receive:" + action);
        if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
            //滅屏 開啟1px activity
            KeepManager.getInstance().startKeep(context);
        } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
            //亮屏 關閉
            KeepManager.getInstance().finishKeep();
        }
    }
}

3.2 Service提權(quán)(降低oom_adj)

創(chuàng)建一個前臺服務用于提高app在按下home鍵之后的進程優(yōu)先級稀拐,startForeground(ID,Notification):使Service成為前臺Service吏祸。 前臺服務需要在通知欄顯示一條通知

  • API level<18:參數(shù)2設置為newNotification(),圖標不會顯示
  • API level>=18 & API level<26:在需要提權(quán)的service A啟動一個InnerService,兩個服務都startForeground钩蚊,且綁定相同的id贡翘,Stop掉InnerService,通知欄圖標被移除
  • API level>=26:必須手動創(chuàng)建通知欄砰逻,無法移除通知欄圖標鸣驱,startForegroundService代替了startService

代碼實現(xiàn):

public class ForegroundService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("deamon", "deamon",
                    NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            if (manager == null)
                return;
            manager.createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, "deamon").setAutoCancel(true).setCategory(
                    Notification.CATEGORY_SERVICE).setOngoing(true).setPriority(
                    NotificationManager.IMPORTANCE_LOW).build();
            startForeground(10, notification);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            //如果 18 以上的設備 啟動一個Service startForeground給相同的id
            //然后結(jié)束那個Service
            startForeground(10, new Notification());
            startService(new Intent(this, InnnerService.class));
        } else {
            startForeground(10, new Notification());
        }
    }

    public static class InnnerService extends Service {

        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(10, new Notification());
            stopSelf();
        }

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

4.進程殺死后拉活方案

4.1 系統(tǒng)廣播拉活

在發(fā)生特定系統(tǒng)事件時,系統(tǒng)會發(fā)出廣播蝠咆,通過在 AndroidManifest 中靜態(tài)注冊對應的廣播監(jiān)聽器踊东,即可在發(fā)生響應事件時拉活。但是從android 7.0開始刚操,對廣播進行了限制闸翅,而且在8.0更加嚴格,8.0以后應該是沒用了菊霜。

官網(wǎng)介紹

4.2 “全家桶”拉活

有多個app在用戶設備上安裝坚冀,只要開啟其中一個就可以將其他的app也拉活。比如手機里裝了手Q鉴逞、QQ空間记某、興趣部落等等司训,那么打開任意一個app后,其他的app也都會被喚醒液南。

普通小廠不太現(xiàn)實壳猜,沒有一系列的app

4.3 Service機制(Sticky)拉活

將 Service 設置為 START_STICKY育灸,利用系統(tǒng)機制在 Service 掛掉后自動拉活

  • START_STICKY: “粘性”掸绞。如果service進程被kill掉,保留service的狀態(tài)為開始狀態(tài)贪婉,但不保留遞送的intent對象畅姊。隨后系統(tǒng)會嘗試重新創(chuàng)建service闪幽,由于服務狀態(tài)為開始狀態(tài),所以創(chuàng)建服務后一定會調(diào)用onStartCommand(Intent,int,int)方法涡匀。如果在此期間沒有任何啟動命令被傳遞到service盯腌,那么參數(shù)Intent將為null。
  • START_NOT_STICKY: “非粘性的”陨瘩。使用這個返回值時腕够,如果在執(zhí)行完onStartCommand后,服務被異常kill掉舌劳,系統(tǒng)不會自動重啟該服務帚湘。
  • START_REDELIVER_INTENT: 重傳Intent。使用這個返回值時甚淡,如果在執(zhí)行完onStartCommand后大诸,服務被異常kill掉,系統(tǒng)會自動重啟該服務贯卦,并將Intent的值傳入资柔。
  • START_STICKY_COMPATIBILITY: START_STICKY的兼容版本,但不保證服務被kill后一定能重啟撵割。

只要 targetSdkVersion 不小于5贿堰,就默認是 START_STICKY。但是某些ROM 系統(tǒng)不會拉活啡彬。并且經(jīng)過測試羹与,Service 第一次被異常殺死后很快被重啟,第二次會比第一次慢庶灿,第三次又會比前一次慢纵搁,一旦在短時間內(nèi) Service 被殺死4-5次,則系統(tǒng)不再拉起往踢。

4.4 賬戶同步拉活

手機系統(tǒng)設置里會有“帳戶”一項功能腾誉,任何第三方APP都可以通過此功能將數(shù)據(jù)在一定時間內(nèi)同步到服務器中去。系統(tǒng)在將APP帳戶同步時,會將未啟動的APP進程拉活

(1)開啟賬戶服務

public class AuthenticationService extends Service {
    private AccountAuthenticator accountAuthenticator;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return accountAuthenticator.getIBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        accountAuthenticator = new AccountAuthenticator(this);
    }

    static class AccountAuthenticator extends AbstractAccountAuthenticator {

        public AccountAuthenticator(Context context) {
            super(context);
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
            return null;
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
                                 String authTokenType, String[] requiredFeatures,
                                 Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
                                         Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
                                   String authTokenType, Bundle options) throws
                NetworkErrorException {
            return null;
        }

        @Override
        public String getAuthTokenLabel(String authTokenType) {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
                                        String authTokenType, Bundle options) throws
                NetworkErrorException {
            return null;
        }

        @Override
        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
                                  String[] features) throws NetworkErrorException {
            return null;
        }
    }
}

在manifest中配置service

  <service android:name=".account.AuthenticationService">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>
            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
        </service>

在xml中添加authenticator.xml

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.dn.daemon.account"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name" />

<!--accountType表示賬戶類型妄辩,必須唯一-->

(2)添加賬戶

public class AccountHelper {
    //與authenticator.xml中accountType一致
    private static final String ACCOUNT_TYPE = "com.dn.daemon.account";
    private static final String CONTENT_AUTHORITY = "com.dn.daemon.provider";

    public static void addAccount(Context context) {
        AccountManager accountManager = (AccountManager) context.getSystemService(
                Context.ACCOUNT_SERVICE);
        Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
        if (accounts.length > 0) {
            //賬戶已存在
            return;
        }
        Account account = new Account("hxl", ACCOUNT_TYPE);
        accountManager.addAccountExplicitly(account, "dn123", new Bundle());//直接添加賬戶
    }
}
......

(3)同步服務

創(chuàng)建一個Service作為同步Service惑灵,并且在onBind返回AbstractThreadedSyncAdapter的getSyncAdapterBinder

public class SyncService extends Service {
    private static final String TAG = "SyncService";
    private SyncAdapter syncAdapter;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return syncAdapter.getSyncAdapterBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        syncAdapter = new SyncAdapter(this, true);
    }

    static class SyncAdapter extends AbstractThreadedSyncAdapter {

        public SyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras, String authority,
                                  ContentProviderClient provider, SyncResult syncResult) {
            Log.e(TAG,"賬戶同步了山上!");
        }
    }
}

在manifest中配置service

        <service android:name=".account.SyncService">
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>
            <meta-data
                android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
        </service>

在xml中添加syncadapter.xml

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.dn.daemon.provider"
    android:accountType="com.dn.daemon.account"
    android:userVisible="false"
    android:supportsUploading="false"
    android:allowParallelSyncs="false"
    android:isAlwaysSyncable="true"
    />
    <!--contentAuthority 系統(tǒng)在進行賬戶同步的時候會查找 此auth的ContentProvider-->
    <!--accountType表示賬戶類型眼耀,與authenticator.xml里要一致-->
    <!-- userVisible 是否在“設置”中顯示-->
    <!-- supportsUploading 是否必須notifyChange通知才能同步-->
    <!-- allowParallelSyncs 允許多個賬戶同時同步-->
    <!--isAlwaysSyncable 設置所有賬號的isSyncable為1-->

(4)創(chuàng)建ContentProvider

public class SyncProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection,
                      @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
                      @Nullable String[] selectionArgs) {
        return 0;
    }
}

在manifest中配置ContentProvider

 <provider
            android:authorities="com.dn.daemon.provider"
            android:name=".account.SyncProvider"
            android:exported="false"
            />

(5)開啟同步

為了達到進程保活的效果佩憾,可以開啟自動同步哮伟。時間間隔雖然設置了1s,但是Android本身為了考慮同步所帶來的消耗和減少喚醒設備的次數(shù)妄帘,1s只是一個參考時間

public class AccountHelper {
    //與authenticator.xml中accountType一致
    private static final String ACCOUNT_TYPE = "com.dn.daemon.account";
    private static final String CONTENT_AUTHORITY = "com.dn.daemon.provider";

    public static void addAccount(Context context) {
        AccountManager accountManager = (AccountManager) context.getSystemService(
                Context.ACCOUNT_SERVICE);
        Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
        if (accounts.length > 0) {
            //賬戶已存在
            return;
        }
        Account account = new Account("hxl", ACCOUNT_TYPE);
        accountManager.addAccountExplicitly(account, "dn123", new Bundle());//直接添加賬戶
    }

    public static void autoSync() {
        Account dongnao = new Account("hxl", ACCOUNT_TYPE);
        //設置同步
        ContentResolver.setIsSyncable(dongnao, CONTENT_AUTHORITY, 1);
        //設置自動同步
        ContentResolver.setSyncAutomatically(dongnao, CONTENT_AUTHORITY, true);
        //設置同步周期
        ContentResolver.addPeriodicSync(dongnao, CONTENT_AUTHORITY, new Bundle(), 1);
    }
}

4.5 JobScheduler拉活

JobScheduler允許在特定狀態(tài)與特定時間間隔周期執(zhí)行任務楞黄。可以利用它的這個特點完成甭胀眨活的功能,效果即開啟一個定時器鬼廓,與普通定時器不同的是其調(diào)度由系統(tǒng)完成。

public class MyJobService extends JobService {
    private static final String TAG = "MyJobService";

    public static void startJob(Context context) {
        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
                Context.JOB_SCHEDULER_SERVICE);
        JobInfo.Builder builder = new JobInfo.Builder(10,
                new ComponentName(context.getPackageName(),
                        MyJobService.class.getName())).setPersisted(true);
        /**
         * I was having this problem and after review some blogs and the official documentation,
         * I realised that JobScheduler is having difference behavior on Android N(24 and 25).
         * JobScheduler works with a minimum periodic of 15 mins.
         *
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            //7.0以上延遲1s執(zhí)行
            builder.setMinimumLatency(1000);
        }else{
            //每隔1s執(zhí)行一次job
            builder.setPeriodic(1000);
        }
        jobScheduler.schedule(builder.build());
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.e(TAG,"開啟job");
        //7.0以上輪詢
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            startJob(this);
        }

        return false;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        return false;
    }
}
  MyJobService.startJob(this);

4.6 推送拉活

根據(jù)終端不同致盟,在小米手機(包括 MIUI)接入小米推送碎税、華為手機接入華為推送。

4.7 Native拉活

Native fork子進程用于觀察當前app主進程的存亡狀態(tài)馏锡。對于5.0以上成功率極低雷蹂,然而目前5.0以下的手機只占10%

4.8 雙進程守護

兩個進程共同運行,如果有其中一個進程被殺杯道,那么另外一個進程就會將被殺的進程重新拉起匪煌。相對于其他方案成功率比較高。

public class LocalService extends Service {
    private MyBinder myBinder;

    class MyBinder extends IMyAidlInterface.Stub{

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

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

    @Override
    public void onCreate() {
        super.onCreate();
        myBinder = new MyBinder();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("deamon", "deamon",
                    NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = (NotificationManager) getSystemService(
                    Context.NOTIFICATION_SERVICE);
            if (manager == null)
                return;
            manager.createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this,
                    "deamon").setAutoCancel(true).setCategory(
                    Notification.CATEGORY_SERVICE).setOngoing(true).setPriority(
                    NotificationManager.IMPORTANCE_LOW).build();
            startForeground(10, notification);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            //如果 18 以上的設備 啟動一個Service startForeground給相同的id
            //然后結(jié)束那個Service
            startForeground(10, new Notification());
            startService(new Intent(this, InnnerService.class));
        } else {
            startForeground(10, new Notification());
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        bindService(new Intent(this, RemoteService.class), new MyServiceConnection(),
                BIND_AUTO_CREATE);
        return super.onStartCommand(intent, flags, startId);
    }

    private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //回調(diào)

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //
            startService(new Intent(LocalService.this, RemoteService.class));
            bindService(new Intent(LocalService.this, RemoteService.class), new MyServiceConnection(),
                    BIND_AUTO_CREATE);
        }
    }

    public static class InnnerService extends Service {

        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(10, new Notification());
            stopSelf();
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
}
public class RemoteService extends Service {
    private MyBinder myBinder;

    class MyBinder extends IMyAidlInterface.Stub {

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

        }
    }

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

    @Override
    public void onCreate() {
        super.onCreate();
        myBinder = new MyBinder();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("deamon", "deamon",
                    NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = (NotificationManager) getSystemService(
                    Context.NOTIFICATION_SERVICE);
            if (manager == null)
                return;
            manager.createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this,
                    "deamon").setAutoCancel(true).setCategory(
                    Notification.CATEGORY_SERVICE).setOngoing(true).setPriority(
                    NotificationManager.IMPORTANCE_LOW).build();
            startForeground(10, notification);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            //如果 18 以上的設備 啟動一個Service startForeground給相同的id
            //然后結(jié)束那個Service
            startForeground(10, new Notification());
            startService(new Intent(this, InnnerService.class));
        } else {
            startForeground(10, new Notification());
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        bindService(new Intent(this, LocalService.class), new MyServiceConnection(),
                BIND_AUTO_CREATE);
        return super.onStartCommand(intent, flags, startId);
    }

    private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            startService(new Intent(RemoteService.this, LocalService.class));
            bindService(new Intent(RemoteService.this, LocalService.class),
                    new MyServiceConnection(), BIND_AUTO_CREATE);
        }
    }

    public static class InnnerService extends Service {

        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(10, new Notification());
            stopSelf();
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
}
@SuppressLint("NewApi")
public class MyJobService extends JobService {
    private static final String TAG = "MyJobService";

    public static void startJob(Context context) {
        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
                Context.JOB_SCHEDULER_SERVICE);
        JobInfo.Builder builder = new JobInfo.Builder(10,
                new ComponentName(context.getPackageName(),
                        MyJobService.class.getName())).setPersisted(true);
        /**
         * I was having this problem and after review some blogs and the official documentation,
         * I realised that JobScheduler is having difference behavior on Android N(24 and 25).
         * JobScheduler works with a minimum periodic of 15 mins.
         *
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //7.0以上延遲1s執(zhí)行
            builder.setMinimumLatency(1000);
        } else {
            //每隔1s執(zhí)行一次job
            builder.setPeriodic(1000);
        }
        jobScheduler.schedule(builder.build());
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.e(TAG, "開啟job");
        //7.0以上輪詢
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            startJob(this);
        }
        //判斷服務是否在運行
        boolean isLocalServiceRun = isServiceRunning(this, LocalService.class.getName());
        boolean isRemoteServiceRun = isServiceRunning(this, RemoteService.class.getName());
        if (!isLocalServiceRun || !isRemoteServiceRun) {

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

    private boolean isServiceRunning(Context context, String serviceName) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(10);
        for (ActivityManager.RunningServiceInfo runningService : runningServices) {
            if (TextUtils.equals(runningService.service.getClassName(), serviceName)) {
                return true;
            }
        }

        return false;
    }

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

manifest設置不同進程

  <service android:name=".service.LocalService"
            android:exported="true"
            android:process=":local"/>
        <service android:name=".service.LocalService$InnnerService"
            android:exported="true"
            android:process=":local"/>
        <service android:name=".service.RemoteService"
            android:exported="true"
            android:process=":remote"/>
        <service android:name=".service.RemoteService$InnnerService"
            android:exported="true"
            android:process=":remote"/>
        <service android:name=".service.MyJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"/>

啟動進程

        startService(new Intent(this, LocalService.class));
        startService(new Intent(this, RemoteService.class));
        MyJobService.startJob(this);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末党巾,一起剝皮案震驚了整個濱河市萎庭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌齿拂,老刑警劉巖擎椰,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異创肥,居然都是意外死亡达舒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門叹侄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巩搏,“玉大人,你說我怎么就攤上這事趾代」岬祝” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長禽捆。 經(jīng)常有香客問我笙什,道長,這世上最難降的妖魔是什么胚想? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任琐凭,我火速辦了婚禮,結(jié)果婚禮上浊服,老公的妹妹穿的比我還像新娘统屈。我一直安慰自己,他們只是感情好牙躺,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布愁憔。 她就那樣靜靜地躺著,像睡著了一般孽拷。 火紅的嫁衣襯著肌膚如雪吨掌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天脓恕,我揣著相機與錄音膜宋,去河邊找鬼。 笑死进肯,一個胖子當著我的面吹牛激蹲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播江掩,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼学辱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了环形?” 一聲冷哼從身側(cè)響起策泣,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抬吟,沒想到半個月后萨咕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡火本,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年危队,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钙畔。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡茫陆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出擎析,到底是詐尸還是另有隱情簿盅,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站桨醋,受9級特大地震影響棚瘟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喜最,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一偎蘸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧返顺,春花似錦禀苦、人聲如沸蔓肯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔗包。三九已至秉扑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間调限,已是汗流浹背舟陆。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耻矮,地道東北人秦躯。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像裆装,于是被迫代替她去往敵國和親踱承。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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