1. 进程是怎么死的
- 系统资源不足回收
- 第三方安全软件杀死
- 用户在设置页面强制结束
- 用户在设置页面的正在运行中结束服务
- 一键清理最近任务列表
2. 进程保活的手段
这里有一个非常不错的博客+github开源项目,里面详细介绍了6.0以下能确保不死的方法 博客地址
目前比较流行的方案有:
- 将服务设置为前台进程。这样可以大大得提高进程的优先级,可以大大的降低被系统回收的概率。缺点是前台服务会有一个去不掉通知出现在通知栏,不过在7.0以下可以利用BUG去掉这个通知。
- 后台播放一段无声的音乐,在程序从后台切入前台时停止播放,从前台切入后台时开始播放。提高进程的优先级
- 定时器定时唤醒,在5.0以上使用JobService,5.0以下使用AlarmManager。这两种方式都可以自动获取到唤醒锁。
- 双进程互相守护,两个进程互相绑定,当某一个进程判断与对方连接断开时,立马开启对方并且继续绑定
- 锁屏时启动一像素Activity,解锁时关闭这个Activity。用于提高锁屏状态下的进程优先级
- SDK互相拉活,比如集成了个推SDK的应用,只要有一个还存活,那么会立马拉起其他所有集成了个推SDK的应用
- 监听触发频繁的系统静态广播,这个在高版本直接无效,很多广播无法静态注册,并且被杀死的程序不会收到静态广播
- 设置厂商的白名单,可以确保不会被杀死
3. 目前采用的几种方案
目前采用了前台服务进程、后台音乐、定时器唤醒、双进程守护、锁屏唤起一像素Activity 五种保活手段
A. 首先给出一些常量的定义,最重要的就是这三个定义
- OPEN_FOREGROUND_SERVICE:是否开启前台服务,因为在7.0以下可以通过BUG来去掉前台服务的通知,所以这里就把开关设置为7.0以下。如果你的程序能够容忍这个通知,可以把这个值设置为常量true
- OPEN_JOB_SCHEDULER_OR_ALARM_MANAGER:是开启JobService定时器还是开启AlarmManager定时器。因为5.0以上JobService的效果比AlarmManager好,所以这里就把开关设置为5.0以上
- MATCH_JOB_SCHEDULER_7:是否手动完成JobService的定时器效果,7.0以上默认定时器的间隔时间最短为15分钟,所以这里需要手动地处理这种情况
public class KeepAliveConstants {
public static final int HIDE_FOREGROUND_SERVICE_STOP_DELAY = 2000; // 隐藏前台通知的延迟时间
public static final int FOREGROUND_NOTIFICATION_ID = 13691; // 前台通知ID
public static final String REMOTE_PROCESS_NAME = ":keepalive"; // 远程保活进程名
public static final long INTERVAL_WAKEUP = 30000L;
public static final String ACTION_PLAY_MUSIC_ON = "ACTION_PLAY_MUSIC_ON"; // 音乐开启广播
public static final String ACTION_PLAY_MUSIC_OFF = "ACTION_PLAY_MUSIC_OFF"; // 音乐关闭广播
public static final String ACTION_FINISH_ONE_PIXEL_ACTIVITY = "ACTION_FINISH_ONE_PIXEL_ACTIVITY"; // 一像素界面关闭广播
public static String FOREGROUND_NOTIFICATION_TITLE = ""; // 前台通知的标题
public static String FOREGROUND_NOTIFICATION_DESCRIPTION = ""; // 前台通知的描述
public static int FOREGROUND_NOTIFICATION_ICON_ID = 0; // 前台通知的图标
// 7.0以下才开启前台服务
public static boolean OPEN_FOREGROUND_SERVICE = Build.VERSION.SDK_INT <= Build.VERSION_CODES.N;
// 5.0以上开启 job scheduler,5.0以下开启 alarm manager
public static boolean OPEN_JOB_SCHEDULER_OR_ALARM_MANAGER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
// 7.0以上需要单独适配 job scheduler
public static boolean MATCH_JOB_SCHEDULER_7 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
public static final boolean MEDIA_PLAYER_POWER = true; // 后台音乐播放省电模式
public static final int MEDIA_PLAYER_DELAY = 10000; // 后台音乐播放间隔时间
}
B. 启用定时器唤醒
程序的入口非常简单,就是根据不同的版本起不同的服务。特别注意这个方法只能被调用一次
public class JuMeiStrategy implements IKeepAliveStrategy {
private static boolean isInit = false;
@Override
public void keepAlive(Context context) {
if (isInit) {
return;
}
try {
if (KeepAliveConstants.OPEN_JOB_SCHEDULER_OR_ALARM_MANAGER) {
LogUtils.i("JuMeiStrategy 大于等于5.0,开启 JobScheduler");
// 用 job scheduler 的方式
Intent intent = new Intent(context, JobHandlerService.class);
Utils.startServiceSafety(context, intent);
} else {
LogUtils.i("JuMeiStrategy 小于5.0,开启 AlarmManager");
// 用 alarm manager 的方式
Intent intent = new Intent(context, AlarmHandlerService.class);
Utils.startServiceSafety(context, intent);
}
isInit = true;
} catch (Exception e) {
e.printStackTrace();
isInit = false;
}
}
}
大于5.0,则启动 JobHandlerService 服务,采用的是 JobService 的定时方式。
- 首先调用 startService,开启双进程守护。这个后面再说
- 开启 JobService,定时调用 startService,如果本地服务或远程服务被杀掉了,则立马启动起来
- 内部针对7.0以上做了手动定时的效果
- 开启前台服务,这个后面再说
@SuppressWarnings(value = {"unchecked", "deprecation"})
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public final class JobHandlerService extends JobService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.i("JobHandlerService 启动");
startService(this);
startJobSchedulerSafety();
return START_STICKY;
}
private void startJobSchedulerSafety() {
try {
JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (mJobScheduler == null) {
return;
}
JobInfo.Builder builder = new JobInfo.Builder(new Random().nextInt(),
new ComponentName(getPackageName(), JobHandlerService.class.getName()));
// 7.0 以上设置 setPeriodic 默认最短时间间隔是 15分钟,因此用 setMinimumLatency 手动实现定时效果
if (KeepAliveConstants.MATCH_JOB_SCHEDULER_7) {
LogUtils.i("JobHandlerService 大于等于7.0,手动实现定时器效果");
builder.setMinimumLatency(KeepAliveConstants.INTERVAL_WAKEUP); //执行的最小延迟时间
builder.setOverrideDeadline(KeepAliveConstants.INTERVAL_WAKEUP); //执行的最长延时时间
builder.setMinimumLatency(KeepAliveConstants.INTERVAL_WAKEUP);
builder.setBackoffCriteria(KeepAliveConstants.INTERVAL_WAKEUP, JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
} else {
LogUtils.i("JobHandlerService 大于5.0小于7.0,自动实现定时器效果");
builder.setPeriodic(KeepAliveConstants.INTERVAL_WAKEUP);
}
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setRequiresCharging(true); // 当插入充电器,执行该任务
mJobScheduler.schedule(builder.build());
LogUtils.i("JobHandlerService 开启Job计划");
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("JobHandlerService 开启Job计划失败");
}
}
private void startService(Context context) {
if (Utils.isServiceRunning(getApplicationContext(), LocalService.class.getName())
&& Utils.isRunningTaskExist(getApplicationContext(), getPackageName() + KeepAliveConstants.REMOTE_PROCESS_NAME)) {
return;
}
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
LogUtils.i("JobHandlerService 开启前台服务");
Utils.startForegroundSafety(this);
}
//启动本地服务
Intent localIntent = new Intent(context, LocalService.class);
Utils.startServiceSafety(context, localIntent);
//启动守护进程
Intent guardIntent = new Intent(context, RemoteService.class);
Utils.startServiceSafety(context, guardIntent);
LogUtils.i("JobHandlerService 开启LocalService和RemoteService");
}
@Override
public boolean onStartJob(JobParameters jobParameters) {
LogUtils.i("JobHandlerService 开始Job");
startService(this);
// 7.0 以上手动重复执行
if (KeepAliveConstants.MATCH_JOB_SCHEDULER_7) {
startJobSchedulerSafety();
}
jobFinished(jobParameters, false);
return true;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
LogUtils.i("JobHandlerService 结束job");
startService(this);
return false;
}
}
针对5.0以下的手机,则启动 AlarmHandlerService,采用 AlarmManager 定时器的效果
- 首先调用 startService 进行双进程守护。这个后面再说
- 只在第一次开启 AlarmManager 进行定时调用 startService,如果本地服务或远程服务被杀掉了,则立马启动起来
- 开启前台服务,这个后面再说
public class AlarmHandlerService extends Service {
private static final int ALARM_MANAGER_REQUEST_CODE = 1000;
private boolean hasStartAlarmManager;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startService(this);
if (!hasStartAlarmManager) {
LogUtils.i("AlarmHandlerService 启动");
startAlarmManagerSafety();
hasStartAlarmManager = true;
} else {
LogUtils.i("AlarmHandlerService 开始调度");
}
return START_STICKY;
}
private void startAlarmManagerSafety() {
try {
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
if (am == null) {
return;
}
Intent intent = new Intent(this, AlarmHandlerService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, ALARM_MANAGER_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (pendingIntent == null) {
return;
}
am.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + KeepAliveConstants.INTERVAL_WAKEUP,
KeepAliveConstants.INTERVAL_WAKEUP, pendingIntent);
LogUtils.i("AlarmHandlerService 开启AlarmManager定时器");
} catch (Exception e) {
e.printStackTrace();
LogUtils.i("AlarmHandlerService 开启AlarmManager定时器失败");
}
}
private void startService(Context context) {
if (Utils.isServiceRunning(getApplicationContext(), LocalService.class.getName())
&& Utils.isRunningTaskExist(getApplicationContext(), getPackageName() + KeepAliveConstants.REMOTE_PROCESS_NAME)) {
return;
}
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
LogUtils.i("AlarmHandlerService 开启前台服务");
Utils.startForegroundSafety(this);
}
//启动本地服务
Intent localIntent = new Intent(context, LocalService.class);
Utils.startServiceSafety(context, localIntent);
//启动守护进程
Intent guardIntent = new Intent(context, RemoteService.class);
Utils.startServiceSafety(context, guardIntent);
LogUtils.i("AlarmHandlerService 开启LocalService和RemoteService");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
C. 采用双进程守护的方式
首先定义进程间通信的 aidl 文件
package com.jm.android.jmkeepalive.aidl;
interface GuardAidl {
//相互唤醒服务
void wakeUp();
}
然后就是本地服务 LocalService,这个服务做了以下的事情
- 刚开启时,立马绑定远程服务 RemoteService
- 绑定远程服务成功后,调用远程服务的接口
- 初始化音乐播放,并在合适的时候播放音乐。这个后面再说
- 注册屏幕广播,为了锁屏时开启一像素Activity。这个后面再说
- 自己开启前台服务。这个后面再说
- 当检测到与远程服务 RemoteService 断开时,重新启动并绑定远程服务
public final class LocalService extends Service {
private ScreenReceiver mScreenReceiver;
private MediaPlayerStatusReceiver mMediaPlayerStateReceiver;
private MyBilder mBilder;
private MediaPlayerUtil mediaPlayerUtil;
@Override
public void onCreate() {
super.onCreate();
if (mBilder == null) {
mBilder = new MyBilder();
}
}
@Override
public IBinder onBind(Intent intent) {
return mBilder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.i("LocalService 开启,初始化音乐");
//播放无声音乐
if (mediaPlayerUtil == null) {
mediaPlayerUtil = new MediaPlayerUtil(this);
}
// 如果是被拉活状态,那么判断是否在后台
if (mediaPlayerUtil.isInitSuccess() && !Utils.isForeground(this)) {
mediaPlayerUtil.play();
}
//像素保活
if (mScreenReceiver == null) {
mScreenReceiver = new ScreenReceiver();
}
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
Utils.registerBroadcastReceiverSafety(this, mScreenReceiver, screenFilter);
//屏幕点亮状态监听,用于单独控制音乐播放
if (mMediaPlayerStateReceiver == null) {
mMediaPlayerStateReceiver = new MediaPlayerStatusReceiver();
}
IntentFilter mediaStatusFilter = new IntentFilter();
mediaStatusFilter.addAction(KeepAliveConstants.ACTION_PLAY_MUSIC_ON);
mediaStatusFilter.addAction(KeepAliveConstants.ACTION_PLAY_MUSIC_OFF);
Utils.registerBroadcastReceiverSafety(this, mMediaPlayerStateReceiver, mediaStatusFilter);
//启用前台服务,提升优先级
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
LogUtils.i("LocalService 开启前台服务");
Utils.startForegroundSafety(this);
}
//绑定守护进程
Intent remoteServiceIntent = new Intent(this, RemoteService.class);
Utils.bindServiceSafety(this, remoteServiceIntent, connection);
LogUtils.i("LocalService 绑定到 RemoteService");
return START_STICKY;
}
private class MediaPlayerStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), KeepAliveConstants.ACTION_PLAY_MUSIC_ON)) {
LogUtils.i("LocalService 收到播放音乐广播");
if (mediaPlayerUtil != null && mediaPlayerUtil.isInitSuccess()) {
mediaPlayerUtil.setPause(false);
mediaPlayerUtil.play();
}
} else if (TextUtils.equals(intent.getAction(), KeepAliveConstants.ACTION_PLAY_MUSIC_OFF)) {
LogUtils.i("LocalService 收到暂停音乐广播");
if (mediaPlayerUtil != null && mediaPlayerUtil.isInitSuccess()) {
mediaPlayerUtil.setPause(true);
mediaPlayerUtil.pause();
}
}
}
}
private final class MyBilder extends GuardAidl.Stub {
@Override
public void wakeUp() throws RemoteException {
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.i("LocalService 断开与 RemoteService 的连接");
Intent remoteServiceIntent = new Intent(LocalService.this, RemoteService.class);
Utils.startServiceSafety(LocalService.this, remoteServiceIntent);
Utils.bindServiceSafety(LocalService.this, remoteServiceIntent, connection);
boolean isForeground = Utils.isForeground(LocalService.this);
Intent it = new Intent(isForeground ? KeepAliveConstants.ACTION_PLAY_MUSIC_OFF : KeepAliveConstants.ACTION_PLAY_MUSIC_ON);
Utils.sendBroadcastSafety(LocalService.this, it);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.i("LocalService 与 RemoteService 连接成功,通知开启前台服务");
try {
GuardAidl guardAidl = GuardAidl.Stub.asInterface(service);
guardAidl.wakeUp();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.i("LocalService 停止,解绑 RemoteService");
unbindService(connection);
unregisterReceiver(mScreenReceiver);
unregisterReceiver(mMediaPlayerStateReceiver);
if (mediaPlayerUtil != null) {
mediaPlayerUtil.destory();
mediaPlayerUtil = null;
}
}
}
然后就是远程服务 RemoteService,做了以下的事情
- 启动时立马绑定本地服务 LocalService
- 收到本地服务 LocalService 的方法调用时,开启前台服务。这个后面再说
- 当检测到与本地服务 LocalService 断开时,立马启动并绑定本地服务。
@SuppressWarnings(value = {"unchecked", "deprecation"})
public final class RemoteService extends Service {
private MyBilder mBilder;
@Override
public void onCreate() {
super.onCreate();
if (mBilder == null) {
mBilder = new MyBilder();
}
}
@Override
public IBinder onBind(Intent intent) {
return mBilder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.i("RemoteService 开启,绑定到 LocalService");
Intent it = new Intent(this, LocalService.class);
Utils.bindServiceSafety(this, it, connection);
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
LogUtils.i("RemoteService 停止,解绑 LocalService");
Utils.unbindServiceSafety(this, connection);
}
private final class MyBilder extends GuardAidl.Stub {
@Override
public void wakeUp() throws RemoteException {
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
// 开启前台服务
LogUtils.i("RemoteService 开启前台服务");
Utils.startForegroundSafety(RemoteService.this);
//隐藏服务通知
LogUtils.i("RemoteService 开启隐藏前台通知服务");
Intent hideForegroundIntent = new Intent(RemoteService.this, HideForegroundService.class);
Utils.startServiceSafety(RemoteService.this, hideForegroundIntent);
}
}
}
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
LogUtils.i("RemoteService 断开与 LocalService 的连接");
Intent localServiceIntent = new Intent(RemoteService.this, LocalService.class);
Utils.startServiceSafety(RemoteService.this, localServiceIntent);
Utils.bindServiceSafety(RemoteService.this, localServiceIntent, connection);
boolean isForeground = Utils.isForeground(RemoteService.this);
Intent it = new Intent(isForeground ? KeepAliveConstants.ACTION_PLAY_MUSIC_OFF : KeepAliveConstants.ACTION_PLAY_MUSIC_ON);
Utils.sendBroadcastSafety(RemoteService.this, it);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtils.i("RemoteService 与 LocalService 连接成功");
}
};
}
C. 后台播放音乐
首先利用 Application 来判断当前程序是否进入了后台,并发送相应的广播
context.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
private int activityCount = 0;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
activityCount++;
if(activityCount == 1){
LogUtils.i("从后台切入前台,发送停止播放音乐广播");
Utils.sendBroadcastSafety(mContext, new Intent(KeepAliveConstants.ACTION_PLAY_MUSIC_OFF));
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
activityCount--;
if (activityCount == 0) {
LogUtils.i("从前台切入后台,发送播放音乐广播");
Utils.sendBroadcastSafety(mContext, new Intent(KeepAliveConstants.ACTION_PLAY_MUSIC_ON));
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
然后定义了音乐播放的封装类
public class MediaPlayerUtil {
private MediaPlayer mediaPlayer;
private Handler handler;
private boolean isPause;//控制暂停
private boolean isInitSuccess;
public MediaPlayerUtil(Context context) {
if (handler == null) {
handler = new Handler();
}
try {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(context, R.raw.novioce);
}
if (mediaPlayer == null) {
isInitSuccess = false;
return;
}
mediaPlayer.setVolume(0f, 0f);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
if (isPause) {
LogUtils.i("MediaPlayerUtil 播放结束,但是被暂停了,不继续播放");
return;
}
if (KeepAliveConstants.MEDIA_PLAYER_POWER) {
if (handler != null) {
LogUtils.i("MediaPlayerUtil 播放结束,省电模式,延迟10秒播放");
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isPause) {
LogUtils.i("MediaPlayerUtil 延迟结束准备播放,但是被暂停了,不继续播放");
return;
}
play();
}
}, KeepAliveConstants.MEDIA_PLAYER_DELAY);
}
} else {
LogUtils.i("MediaPlayerUtil 播放结束,不省电,继续播放");
play();
}
}
});
isInitSuccess = true;
} catch (Exception e) {
e.printStackTrace();
isInitSuccess = false;
}
}
public void play() {
if (isInitSuccess && mediaPlayer != null && !mediaPlayer.isPlaying()) {
try {
mediaPlayer.start();
LogUtils.i("MediaPlayerUtil 开始播放");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void pause() {
if (isInitSuccess && mediaPlayer != null && mediaPlayer.isPlaying()) {
try {
mediaPlayer.pause();
LogUtils.i("MediaPlayerUtil 暂停播放");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void setPause(boolean pause) {
isPause = pause;
}
public boolean isInitSuccess() {
return isInitSuccess;
}
public void destory() {
try {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
isInitSuccess = false;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后在初始化本地服务 LocalService 时,初始化音乐播放器,并且判断当前如果已经进入了后台的话,则立刻播放音乐。这是因为如果程序已经在后台被杀死时,远程服务会重启本地服务,此时程序就处于后台,就应该播放音乐来继续保活。
//播放无声音乐
if (mediaPlayerUtil == null) {
mediaPlayerUtil = new MediaPlayerUtil(this);
}
// 如果是被拉活状态,那么判断是否在后台
if (mediaPlayerUtil.isInitSuccess() && !Utils.isForeground(this)) {
mediaPlayerUtil.play();
}
然后定义一个广播接收者,控制音乐的播放
private class MediaPlayerStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), KeepAliveConstants.ACTION_PLAY_MUSIC_ON)) {
LogUtils.i("LocalService 收到播放音乐广播");
if (mediaPlayerUtil != null && mediaPlayerUtil.isInitSuccess()) {
mediaPlayerUtil.setPause(false);
mediaPlayerUtil.play();
}
} else if (TextUtils.equals(intent.getAction(), KeepAliveConstants.ACTION_PLAY_MUSIC_OFF)) {
LogUtils.i("LocalService 收到暂停音乐广播");
if (mediaPlayerUtil != null && mediaPlayerUtil.isInitSuccess()) {
mediaPlayerUtil.setPause(true);
mediaPlayerUtil.pause();
}
}
}
}
当本地服务 LocalService 或远程服务 RemoteService 与对方断开时,根据处于前台或后台的情况,决定发送播放或暂停的广播
boolean isForeground = Utils.isForeground(LocalService.this);
Intent it = new Intent(isForeground ? KeepAliveConstants.ACTION_PLAY_MUSIC_OFF : KeepAliveConstants.ACTION_PLAY_MUSIC_ON);
Utils.sendBroadcastSafety(LocalService.this, it);
在 onDestory 中,销毁音乐播放器和对应的广播接收者
unregisterReceiver(mMediaPlayerStateReceiver);
if (mediaPlayerUtil != null) {
mediaPlayerUtil.destory();
mediaPlayerUtil = null;
}
D. 锁屏开启一像素Activity
首先创建监听锁屏的广播。屏幕关闭则开启一像素Activity,屏幕亮起则发送关闭Activity的广播
@SuppressWarnings(value = {"unchecked", "deprecation"})
public final class ScreenReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) { //屏幕关闭的时候接受到广播
// 开启一像素Activity
LogUtils.i("ScreenReceiver 屏幕熄灭,开启一像素Activity");
Intent it = new Intent(context, OnePixelActivity.class);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Utils.startActivitySafety(context, it);
} else if (TextUtils.equals(intent.getAction(), Intent.ACTION_SCREEN_ON)) { //屏幕打开的时候发送广播 结束一像素
// 结束一像素Activity
LogUtils.i("ScreenReceiver 屏幕亮起,发送关闭一像素Activity广播");
Utils.sendBroadcastSafety(context, new Intent(KeepAliveConstants.ACTION_FINISH_ONE_PIXEL_ACTIVITY));
}
}
}
然后申明一像素Activity,并在启动时判断当前屏幕是否已经亮起,如果已经亮起则立马结束该Activity。解决广播延迟带来的交互上的问题
public final class OnePixelActivity extends Activity {
//注册广播接受者 当屏幕开启结果成功结束一像素的activity
private BroadcastReceiver br;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LogUtils.i("OnePixelActivity 启动一像素Activity");
//设定一像素的activity
setOnePixel();
//在一像素activity里注册广播接受者 接受到广播结束掉一像素
br = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
LogUtils.i("OnePixelActivity 接受到广播,关闭一像素Activity");
Utils.finishSafety(OnePixelActivity.this);
}
};
Utils.registerBroadcastReceiverSafety(this, br, new IntentFilter(KeepAliveConstants.ACTION_FINISH_ONE_PIXEL_ACTIVITY));
checkScreenOn();
}
private void setOnePixel(){
Window window = getWindow();
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(params);
}
@Override
protected void onDestroy() {
Utils.unregisterBroadcastReceiverSafety(this, br);
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
checkScreenOn();
}
private void checkScreenOn() {
if (Utils.isScreenOn(this)) {
LogUtils.i("OnePixelActivity 关闭一像素Activity");
Utils.finishSafety(this);
}
}
}
E. 开启前台服务
开启的方法很简单,前面所有的服务都有对应的代码
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
LogUtils.i("LocalService 开启前台服务");
Utils.startForegroundSafety(this);
}
然后利用7.0之前的BUG,新启一个服务并用同一个 id 开启前台服务,然后立刻关闭前台服务,就能将通知栏上的通知去掉。首先定义这个需要隐藏通知的服务
public class HideForegroundService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.i("隐藏前台服务通知启动");
Utils.startForegroundSafety(this);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
LogUtils.i("隐藏前台服务结束");
Utils.stopForegroundSafety(HideForegroundService.this, true);
Utils.stopServiceSafety(HideForegroundService.this);
}
}, KeepAliveConstants.HIDE_FOREGROUND_SERVICE_STOP_DELAY);
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
然后在所有服务都开启过前台服务之后,开启这个隐藏通知的服务。这里选择本地服务 LocalService 调用远程服务 RemoteService 的方法时触发
private final class MyBilder extends GuardAidl.Stub {
@Override
public void wakeUp() throws RemoteException {
if (KeepAliveConstants.OPEN_FOREGROUND_SERVICE) {
// 开启前台服务
LogUtils.i("RemoteService 开启前台服务");
Utils.startForegroundSafety(RemoteService.this);
//隐藏服务通知
LogUtils.i("RemoteService 开启隐藏前台通知服务");
Intent hideForegroundIntent = new Intent(RemoteService.this, HideForegroundService.class);
Utils.startServiceSafety(RemoteService.this, hideForegroundIntent);
}
}
}
F. 代码中所有安全启动Activity、启动与绑定Service,安全注册与发送广播等工具类
相关工具类
public class Utils {
public static boolean isMainProcess(Context context) {
int pid = android.os.Process.myPid();
String processName = "";
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (mActivityManager == null) {
return false;
}
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
processName = appProcess.processName;
break;
}
}
String packageName = context.getPackageName();
return TextUtils.equals(processName, packageName);
}
public static void startForegroundSafety(Service service) {
if (service == null) {
return;
}
try {
Intent intent = new Intent(service.getApplicationContext(), NotificationClickReceiver.class);
intent.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
Notification notification = NotificationUtils.createNotification(
service.getApplicationContext(),
KeepAliveConstants.FOREGROUND_NOTIFICATION_TITLE,
KeepAliveConstants.FOREGROUND_NOTIFICATION_DESCRIPTION,
KeepAliveConstants.FOREGROUND_NOTIFICATION_ICON_ID, intent);
service.startForeground(KeepAliveConstants.FOREGROUND_NOTIFICATION_ID, notification);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void stopForegroundSafety(Service service, boolean removeNotification) {
if (service == null) {
return;
}
try {
service.stopForeground(removeNotification);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void startServiceSafety(Context context, Intent intent) {
if (context == null || intent == null) {
return;
}
try {
context.startService(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void stopServiceSafety(Service service) {
if (service == null) {
return;
}
try {
service.stopSelf();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void startActivitySafety(Context context, Intent intent) {
if (context == null || intent == null) {
return;
}
try {
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void finishSafety(Activity activity) {
if (activity == null) {
return;
}
try {
if (!activity.isFinishing()) {
activity.finish();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void sendBroadcastSafety(Context context, Intent intent) {
if (context == null || intent == null) {
return;
}
try {
context.sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void registerBroadcastReceiverSafety(Context context, BroadcastReceiver receiver,
IntentFilter filter) {
if (context == null || receiver == null || filter == null) {
return;
}
try {
context.registerReceiver(receiver, filter);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void unregisterBroadcastReceiverSafety(Context context, BroadcastReceiver receiver) {
if (context == null || receiver == null) {
return;
}
try {
context.unregisterReceiver(receiver);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void bindServiceSafety(Context context, Intent intent, ServiceConnection connection) {
if (context == null || intent == null || connection == null) {
return;
}
try {
context.bindService(intent, connection, Context.BIND_ABOVE_CLIENT);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void unbindServiceSafety(Context context, ServiceConnection connection) {
if (context == null || connection == null) {
return;
}
try {
context.unbindService(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean isServiceRunning(Context ctx, String className) {
boolean isRunning = false;
try {
if (ctx == null || TextUtils.isEmpty(className)) {
return false;
}
ActivityManager activityManager = (ActivityManager) ctx
.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager == null) {
return false;
}
List servicesList = activityManager
.getRunningServices(Integer.MAX_VALUE);
if (servicesList == null) {
return false;
}
Iterator l = servicesList.iterator();
while (l.hasNext()) {
ActivityManager.RunningServiceInfo si = l.next();
if (TextUtils.equals(className, si.service.getClassName())) {
isRunning = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isRunning;
}
public static boolean isRunningTaskExist(Context context, String processName) {
try {
if (context == null || TextUtils.isEmpty(processName)) {
return false;
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return false;
}
List processList = am.getRunningAppProcesses();
if (processList == null) {
return false;
}
for (ActivityManager.RunningAppProcessInfo info : processList) {
if (TextUtils.equals(info.processName, processName)) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static boolean isForeground(Context context) {
if (context == null) {
return false;
}
try {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return false;
}
List tasks = am.getRunningTasks(1);
if (tasks != null && !tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (TextUtils.equals(topActivity.getPackageName(), context.getPackageName())) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static boolean isScreenOn(Context context) {
if (context == null) {
return false;
}
try {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null) {
return false;
}
return pm.isScreenOn();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
通知工具类
public class NotificationUtils {
private NotificationManager manager;
private String id;
private String name;
private Context context;
private NotificationChannel channel;
private NotificationUtils(Context context) {
this.context = context;
id = context.getPackageName();
name = context.getPackageName();
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void createNotificationChannel() {
if (channel == null) {
channel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH);
channel.enableVibration(false);
channel.enableLights(false);
channel.enableVibration(false);
channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
getManager().createNotificationChannel(channel);
}
}
private NotificationManager getManager() {
if (manager == null) {
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
return manager;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private Notification.Builder getChannelNotification(String title, String content, int icon, Intent intent) {
//PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return new Notification.Builder(context, id)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(icon)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
}
private NotificationCompat.Builder getNotification_25(String title, String content, int icon, Intent intent) {
//PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Builder(context, id)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(icon)
.setAutoCancel(true)
.setVibrate(new long[]{0})
.setContentIntent(pendingIntent);
}
public static Notification createNotification(@NonNull Context context, @NonNull String title, @NonNull String content, @NonNull int icon, @NonNull Intent intent) {
NotificationUtils notificationUtils = new NotificationUtils(context);
Notification notification = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationUtils.createNotificationChannel();
notification = notificationUtils.getChannelNotification(title, content, icon, intent).build();
} else {
notification = notificationUtils.getNotification_25(title, content, icon, intent).build();
}
return notification;
}
}
G. 功能清单的配置
- JobHandlerService 必须要配置权限
- RemoteService 的进程必须要和 KeepAliveConstants.REMOTE_PROCESS_NAME 的值一致
- OnePixelActivity 最好配置 singleInstance 模式,否则屏幕点亮时会关闭一像素 Activity,此时如果当前程序正在后台,会将程序弹到前台。最好设置 excludeFromRecents 属性使新栈不出现在最近任务列表。最好设置主题透明,否则屏幕点亮关闭一像素 Activity 时会有一抹黑。