最近在使用高德地图实时获取定位时遇到了个问题,锁屏后一段时间(5~10分钟左右)后程序会被系统杀死,为了保活,特研究了下进程保活机制。

0、基本操作和概念

针对root过的手机,可以通过下列命令查看内存阈值:

adb shell
su
cat /sys/module/lowmemorykiller/parameters/minfree
会出现6个数字,从小到大

某个数字 * 4KB / 1024KB = 某某MB;
这里的4kb是Linux中一页(one page)的大小;
得到的某某MB就是当系统内存小于某某M的时候,就杀死某些等级的进程。—俗称内存阈值

6个等级分别对应:
前台进程——可见进程——服务进程——后台进程——空进程

1、WakeLock 配合 PowerManager

从源头上把控,不让界面锁屏。

WakeLock mWakeLock;
     
        //申请设备电源锁
        public static void acquireWakeLock(Context context)
        {
            if (null == mWakeLock)
            {
                PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
                mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "WakeLock");
                if (null != mWakeLock)
                {
                    mWakeLock.acquire();
                }
            }
        }
        //释放设备电源锁
        public static void releaseWakeLock()
        {
            if (null != mWakeLock)
            {
                mWakeLock.release();
                mWakeLock = null;
            }

此法于我无效,继续探索。

2、后台播放无声音乐

这个方法在持续进行位置获取上使用过了,有效,但是仍不能保证我的主进程活着,主进程挂了,音乐播放自然也无效了。

3、1像素Activity保活

OnePixelReceiver.java

package com.jsc4.androidcppsafe.onePixelKeepLive;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * 监听屏幕状态的广播
 */
public class OnePixelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {    //屏幕关闭启动1像素Activity
            Intent it = new Intent(context, OnePixelActivity.class);
            it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(it);
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {   //屏幕打开 结束1像素
            context.sendBroadcast(new Intent("finish"));
            Intent main = new Intent(Intent.ACTION_MAIN);
            main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            main.addCategory(Intent.CATEGORY_HOME);
            context.startActivity(main);
        }
    }
}

OnePixelActivity.java

package com.jsc4.androidcppsafe.onePixelKeepLive;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;

/**
 * Created by Administrator on 2017/7/10.
 */
public class OnePixelActivity extends Activity {

    private BroadcastReceiver endReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置1像素
        Window window = getWindow();
        window.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x = 0;
        params.y = 0;
        params.height = 1;
        params.width = 1;
        window.setAttributes(params);

        //结束该页面的广播
        endReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                finish();
            }
        };
        registerReceiver(endReceiver, new IntentFilter("finish"));
        //检查屏幕状态
        checkScreen();
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkScreen();
        Log.i("djtest", "OnePixelActivity: onResume");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("djtest", "OnePixelActivity: onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("djtest", "OnePixelActivity: onDestroy");
    }

    /**
     * 检查屏幕状态  isScreenOn为true  屏幕“亮”结束该Activity
     */
    private void checkScreen() {
        PowerManager pm = (PowerManager) OnePixelActivity.this.getSystemService(Context.POWER_SERVICE);
        boolean isScreenOn = pm.isScreenOn();
        if (isScreenOn) {
            finish();
        }
    }

}

在MainActivity.java的onCreate中:

//注册监听屏幕开启和关闭的广播
        OnePixelReceiver mOnepxReceiver = new OnePixelReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.SCREEN_OFF");
        intentFilter.addAction("android.intent.action.SCREEN_ON");
        intentFilter.addAction("android.intent.action.USER_PRESENT");
        registerReceiver(mOnepxReceiver, intentFilter);

4、双服务保活

参考:
https://github.com/linliangliang/TwoProcess https://www.cnblogs.com/lulj/p/7161317.html

(1)在main文件夹上右键,新建AIDL文件IMyAidlInterface.aidl:

// IMyAidlInterface.aidl
package com.jsc4.androidcppsafe;

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

interface IMyAidlInterface {
    /**
     * 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);

      String getServiceName();
}

(2)在java文件夹下新建KeepServiceAlive文件夹,下面创建3个文件:
注意:必须点了运行工程,才能自动导入IMyAidlInterface类。
LocalService.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

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

import com.jsc4.df.IMyAidlInterface;

import java.util.Timer;
import java.util.TimerTask;


/**
 * Created by lenovo on 2019/8/5.
 */

public class LocalService extends Service {

    private final static String TAG = LocalService.class.getName();
    private static int count = 0;
    private static Timer timer = null;
    private MyBinder mBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                Log.i("LocalService", "connected with " + iMyAidlInterface.getServiceName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (Constans.INSTANCE.isOpenServiceDefend == 1) {
                Log.i(TAG, "链接断开,重新启动 RemoteService......");
                startService(new Intent(LocalService.this, RemoteService.class));
                bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
            }

        }
    };

    public LocalService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i(TAG, "--" + count++);
                }
            }, 0, 1000);
        }


    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Constans.INSTANCE.isOpenServiceDefend == 1) {
            Log.i(TAG, "本地服务启动......");
            startService(new Intent(LocalService.this, RemoteService.class));

            bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
        }
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new MyBinder();
        return mBinder;
    }

    private class MyBinder extends IMyAidlInterface.Stub {
        @Override
        public String getServiceName() throws RemoteException {
            return LocalService.class.getName();
        }

    }

    @Override
    public void onDestroy() {
        // TODO 自动生成的方法存根
        super.onDestroy();
        try {
            if (timer != null) {
                timer.cancel();
                timer=null;
            }
            unbindService(connection);
        } catch (Exception e) {
            System.exit(0);
        }

    }
}

RemoteService.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

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

import com.jsc4.df.IMyAidlInterface;

import java.util.Timer;
import java.util.TimerTask;


/**
 * Created by lenovo on 2019/8/5.
 */

public class RemoteService extends Service {
    private MyBinder mBinder;
    private static Timer timer = null;
    private static int count = 0;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                Log.i("RemoteService", "connected with " + iMyAidlInterface.getServiceName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (Constans.INSTANCE.isOpenServiceDefend == 1) {
                Toast.makeText(RemoteService.this, "链接断开,重新启动 LocalService", Toast.LENGTH_LONG).show();
                startService(new Intent(RemoteService.this, LocalService.class));
                bindService(new Intent(RemoteService.this, LocalService.class), connection, Context.BIND_IMPORTANT);
            }
        }
    };

    public RemoteService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i("RemoteService", "==" + count++);
                }
            }, 0, 2000);
        }

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Constans.INSTANCE.isOpenServiceDefend == 1) {
            Log.i("RemoteService", "RemoteService 启动...");
            bindService(new Intent(this, LocalService.class), connection, Context.BIND_IMPORTANT);
        }
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new MyBinder();
        return mBinder;
    }

    private class MyBinder extends IMyAidlInterface.Stub {

        @Override
        public String getServiceName() throws RemoteException {
            return RemoteService.class.getName();
        }
    }

    @Override
    public void onDestroy() {
        // TODO 自动生成的方法存根
        super.onDestroy();
        try {
            if (timer != null) {
                timer.cancel();
                timer=null;
            }
            unbindService(connection);
        } catch (Exception e) {
            System.exit(0);
        }
    }
}

Constans.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

/**
 * Created by lenovo on 2019/8/5.
 */

public enum Constans {
    //枚举类型实现单例
    INSTANCE;
    public static int openServiceDefend = 1;
    public static int stopServiceDefend = 0;
    public static int isOpenServiceDefend = 1;//是否开启进程守护,默认开启

    public static void setIsOpenServiceDefend(int isOpenServiceDefend) {
        Constans.isOpenServiceDefend = isOpenServiceDefend;
    }
}

(3)在AndroidMenifest.xml中添加:

<service
            android:name=".KeepServiceAlive.LocalService"
            android:enabled="true"
            android:exported="true"/>

        <service
            android:name=".KeepServiceAlive.RemoteService"
            android:enabled="true"
            android:exported="true"
            android:process=":RemoteProcess"/>

(4)在MainActivity.java中:

//********************************************************************************************************************************************
// 双进程保活机制 相关函数
//********************************************************************************************************************************************
    private void startBackService() {

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

    private void stopBackService() {
        stopService(new Intent(this, LocalService.class));
    }

    private void stopRemoteService() {
        stopService(new Intent(this, RemoteService.class));
    }

    private void endAllService() {
        Constans.INSTANCE.isOpenServiceDefend = Constans.INSTANCE.stopServiceDefend;//结束进程守护
        stopRemoteService();
        stopBackService();
    }
在oncreate里,添加:
// 启动双进程保活机制
startBackService();

在ondestroy里,添加:
endAllService();// 结束双进程守护

5、前台服务保活

这是我重点想说的功能,该功能亲测有效。
注意:API>26后无法隐藏通知。

ForegroundService.java

package com.jsc4.androidcppsafe.ForegroundServiceKeepLive;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import javax.security.auth.login.LoginException;

public class ForegroundService extends Service {

    private static final int SERVICE_ID = 1;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("djtest", "ForegroundService:  前台服务创建了");

        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ // 4.3以下
            startForeground(SERVICE_ID, new Notification());
        }else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O){ // 4.3-7.0
            startForeground(SERVICE_ID, new Notification());
            startService(new Intent(this, InnerService.class));
        }else{ // 8.0及以上
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel("channel", "andy", NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel);
            Notification notification = new NotificationCompat.Builder(this, "channel").build();
            startForeground(SERVICE_ID, notification);
        }
    }

    public static class InnerService extends Service{

        @Override
        public void onCreate() {
            super.onCreate();
            Log.i("djtest", "ForegroundService: InnerService服务创建了");

            startForeground(SERVICE_ID, new Notification());
            stopSelf();
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {

            return null;
        }
    }
}

AndroidMenifest.xml

<service android:name=".ForegroundServiceKeepLive.ForegroundService"/>
        <service android:name=".ForegroundServiceKeepLive.ForegroundService$InnerService"/>

MainActivity.java的oncreate中:

startService(new Intent(this, ForegroundService.class));

6、自启动管理

这是纯粹手机上设置。
将启动管理中的你的APP选择为手动控制。