Android进程保活终极方案总结

Android进程保活主要包括两个方面:

  1. 提高进程的优先级,降低被杀死的概率
  2. 在进程被杀死后拉活

1.进程优先级

Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略低的进程,依此类推,以回收系统资源。

重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程):

1.1 前台进程

用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

  • 托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
  • 托管某个 Service,后者绑定到用户正在交互的 Activity
  • 托管正在“前台”运行的 Service(服务已调用 startForeground())
  • 托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
  • 托管正执行其 onReceive() 方法的 BroadcastReceiver

通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

1.2 可见进程

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:

  • 托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
  • 托管绑定到可见(或前台)Activity 的 Service。

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

1.3 服务进程

  • 正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。

尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

1.4 后台进程

  • 包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。

这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅 Activity文档。

1.5 空进程

  • 不含任何活动应用组件的进程。

保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。


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

为什么引入LowMemoryKiller?

进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android的设计理念之一。这个机制会带来一个问题,每个进程都有自己独立的内存地址空间,随着应用打开数量的增多,系统已使用的内存越来越大,就很有可能导致系统内存不足。为了解决这个问题,系统引入LowmemoryKiller(简称lmk)管理所有进程,根据一定策略来kill某个进程并释放占用的内存,保证系统的正常运行。

LowMemoryKiller的基本原理

所有应用进程都是从zygote孵化出来的,记录在AMS中mLruProcesses列表中,由AMS进行统一管理,AMS中会根据进程的状态更新进程对应的oom_adj值,这个值会通过文件传递到kernel中去,kernel有个低内存回收机制,在内存达到一定阀值时会触发清理oom_adj值高的进程腾出更多的内存空间。

什么是oom_adj?

它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj的作用,你只需要记住以下几点即可:

  • 进程的oom_adj越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收
  • 普通app进程的oom_adj>=0,系统进程的oom_adj才可能<0

minfree 阈值

存放6个数值,每个数表示内存页面数,单位是page(一个页面4kb)

查看方式:

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

得到的数值为:18432,23040,27648,32256,36864,46080这6个数值分别代表android系统回收6种进程的阈值,这么看不方便查看,转换为M会更直观,这6个数值的单位为page 1page = 4K,所以通过 数值*4/1024就能转换为M:72M,90M,108M,126M,144M,180M,也就是说1.前台进程(foreground),2.可见进程(visible),3.次要服务(secondary server),4.后台进程(hidden),5.内容供应节点(content provider),6.空进程(empty)这6类进程进行回收的内存阈值分别为72M,90M,108M,126M,144M,180M

oom_adj值

oom_adj的值越小,进程的优先级越高

获取进程oom_adj方式:

cat /proc/进程id/oom_adj
adj级别 解释
UNKNOWN_ADJ 16 预留的最低级别,一般对于缓存的进程才有可能设置成这个级别
CACHED_APP_MAX_ADJ 15 缓存进程,空进程,在内存不足的情况下就会优先被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 高权重进程
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 系统进程
NATIVE_ADJ -17 系统起的Native进程

(上表的数字可能在不同系统会有一定的出入)

内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程. 因此Android开始结束优先级最低的空进程,即当可用内存小于180MB(46080)


3.提高进程优先级方案

3.1 Activity提权(降低oom_adj)

原理:

  • 监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素透明的 Activity,在用户解锁时将 Activity 销毁掉,从而达到提高进程优先级的作用,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。

代码实现:

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提权(降低oom_adj)

创建一个前台服务用于提高app在按下home键之后的进程优先级,startForeground(ID,Notification):使Service成为前台Service。 前台服务需要在通知栏显示一条通知

  • API level<18:参数2设置为newNotification(),图标不会显示
  • API level>=18 & API level<26:在需要提权的service A启动一个InnerService,两个服务都startForeground,且绑定相同的id,Stop掉InnerService,通知栏图标被移除
  • API level>=26:必须手动创建通知栏,无法移除通知栏图标,startForegroundService代替了startService

代码实现:

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
            //然后结束那个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 系统广播拉活

在发生特定系统事件时,系统会发出广播,通过在 AndroidManifest 中静态注册对应的广播监听器,即可在发生响应事件时拉活。但是从android 7.0开始,对广播进行了限制,而且在8.0更加严格,8.0以后应该是没用了。

官网介绍

4.2 “全家桶”拉活

有多个app在用户设备上安装,只要开启其中一个就可以将其他的app也拉活。比如手机里装了手Q、QQ空间、兴趣部落等等,那么打开任意一个app后,其他的app也都会被唤醒。

普通小厂不太现实,没有一系列的app

4.3 Service机制(Sticky)拉活

将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活

  • START_STICKY: “粘性”。如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
  • START_NOT_STICKY: “非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
  • START_REDELIVER_INTENT: 重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
  • START_STICKY_COMPATIBILITY: START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

只要 targetSdkVersion 不小于5,就默认是 START_STICKY。但是某些ROM 系统不会拉活。并且经过测试,Service 第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内 Service 被杀死4-5次,则系统不再拉起。

4.4 账户同步拉活

手机系统设置里会有“帐户”一项功能,任何第三方APP都可以通过此功能将数据在一定时间内同步到服务器中去。系统在将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)同步服务

创建一个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 系统在进行账户同步的时候会查找 此auth的ContentProvider-->
    <!--accountType表示账户类型,与authenticator.xml里要一致-->
    <!-- userVisible 是否在“设置”中显示-->
    <!-- supportsUploading 是否必须notifyChange通知才能同步-->
    <!-- allowParallelSyncs 允许多个账户同时同步-->
    <!--isAlwaysSyncable 设置所有账号的isSyncable为1-->

(4)创建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本身为了考虑同步所带来的消耗和减少唤醒设备的次数,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允许在特定状态与特定时间间隔周期执行任务。可以利用它的这个特点完成保活的功能,效果即开启一个定时器,与普通定时器不同的是其调度由系统完成。

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执行
            builder.setMinimumLatency(1000);
        }else{
            //每隔1s执行一次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 推送拉活

根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送。

4.7 Native拉活

Native fork子进程用于观察当前app主进程的存亡状态。对于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
            //然后结束那个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) {
            //回调

        }

        @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
            //然后结束那个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执行
            builder.setMinimumLatency(1000);
        } else {
            //每隔1s执行一次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);
发布了82 篇原创文章 · 获赞 155 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/hxl517116279/article/details/90815307