Android:后端保活

作者 : 开心源码 本文共28482个字,预计阅读时间需要72分钟 发布时间: 2022-05-12 共202人阅读

1. 进程是怎样死的

  1. 系统资源不足回收
  2. 第三方安全软件杀死
  3. 客户在设置页面强制结束
  4. 客户在设置页面的正在运行中结束服务
  5. 一键清除最近任务列表

2. 进程保活的手段

这里有一个非常不错的博客+github开源项目,里面详细详情了6.0以下能确保不死的方法 博客地址

目前比较流行的方案有:

  1. 将服务设置为前端进程。这样可以大大得提高进程的优先级,可以大大的降低被系统回收的概率。缺点是前端服务会有一个去不掉通知出现在通知栏,不过在7.0以下可以利用BUG去掉这个通知。
  2. 后端播放一段无声的音乐,在程序从后端切入前端时中止播放,从前端切入后端时开始播放。提高进程的优先级
  3. 定时器定时唤醒,在5.0以上使用JobService,5.0以下使用AlarmManager。这两种方式都可以自动获取到唤醒锁。
  4. 双进程互相守护,两个进程互相绑定,当某一个进程判断与对方连接断开时,立马开启对方并且继续绑定
  5. 锁屏时启动一像素Activity,解锁时关闭这个Activity。用于提高锁屏状态下的进程优先级
  6. SDK互相拉活,比方集成了个推SDK的应用,只需有一个还存活,那么会立马拉起其余所有集成了个推SDK的应用
  7. 监听触发频繁的系统静态广播,这个在高版本直接无效,很多广播无法静态注册,并且被杀死的程序不会收到静态广播
  8. 设置厂商的白名单,可以确保不会被杀死

3. 目前采用的几种方案

目前采用了前端服务进程、后端音乐、定时器唤醒、双进程守护、锁屏唤起一像素Activity 五种保活手段

A. 首先给出少量常量的定义,最重要的就是这三个定义

  1. OPEN_FOREGROUND_SERVICE:能否开启前端服务,由于在7.0以下可以通过BUG来去掉前端服务的通知,所以这里就把开关设置为7.0以下。假如你的程序能够容忍这个通知,可以把这个值设置为常量true
  2. OPEN_JOB_SCHEDULER_OR_ALARM_MANAGER:是开启JobService定时器还是开启AlarmManager定时器。由于5.0以上JobService的效果比AlarmManager好,所以这里就把开关设置为5.0以上
  3. 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 的定时方式。

  1. 首先调用 startService,开启双进程守护。这个后面再说
  2. 开启 JobService,定时调用 startService,假如本地服务或者远程服务被杀掉了,则立马启动起来
  3. 内部针对7.0以上做了手动定时的效果
  4. 开启前端服务,这个后面再说
@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 定时器的效果

  1. 首先调用 startService 进行双进程守护。这个后面再说
  2. 只在第一次开启 AlarmManager 进行定时调用 startService,假如本地服务或者远程服务被杀掉了,则立马启动起来
  3. 开启前端服务,这个后面再说
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,这个服务做了以下的事情

  1. 刚开启时,立马绑定远程服务 RemoteService
  2. 绑定远程服务成功后,调用远程服务的接口
  3. 初始化音乐播放,并在合适的时候播放音乐。这个后面再说
  4. 注册屏幕广播,为了锁屏时开启一像素Activity。这个后面再说
  5. 自己开启前端服务。这个后面再说
  6. 当检测到与远程服务 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,做了以下的事情

  1. 启动时立马绑定本地服务 LocalService
  2. 收到本地服务 LocalService 的方法调用时,开启前端服务。这个后面再说
  3. 当检测到与本地服务 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. 功能列表的配置

  1. JobHandlerService 必需要配置权限
  2. RemoteService 的进程必需要和 KeepAliveConstants.REMOTE_PROCESS_NAME 的值一致
  3. 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:后端保活

发表回复