最近在使用高德地图实时获取定位时遇到了个问题,锁屏后一段时间(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
从源头上把控,不让界面锁屏。
https://blog.csdn.net/IO_Field/article/details/49591429
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中:
6、自启动管理
这是纯粹手机上设置。
将启动管理中的你的APP选择为手动控制。