Android:后端保活
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<ActivityManager.RunningServiceInfo> servicesList = activityManager .getRunningServices(Integer.MAX_VALUE); if (servicesList == null) { return false; } Iterator<ActivityManager.RunningServiceInfo> 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<ActivityManager.RunningAppProcessInfo> 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<ActivityManager.RunningTaskInfo> 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 时会有一抹黑。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jm.android.jmkeepalive"> <application> <service android:name=".service.JobHandlerService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name=".service.AlarmHandlerService"/> <service android:name=".service.LocalService" /> <service android:name=".service.RemoteService" android:process=":keepalive" /> <service android:name=".service.HideForegroundService" /> <activity android:name=".activity.OnePixelActivity" android:excludeFromRecents="true" android:launchMode="singleInstance" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> </application></manifest>
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android:后端保活