Android应用/进程保活策略总结

Android 应用/进程保活策略总结

1.将Service设置为前台服务

思路:启用前台服务,主要是startForeground() 。
保活程度:一般情况下不被杀,部分定制ROM会在应用切到后台即杀 ,会被 用户手动杀进程(force stop)杀死。
使用场景:大部分音乐播放器通知栏的实现,可以保证后台听歌时应用正常运行。

2.在service的onstart方法里返回 STATR_STICK

思路:其实就是onStartCommand中返回STATR_STICK
保活程度:有次数和时间的限制 ,会被 force stop 杀死

3.添加Manifest文件属性值为android:persistent=“true”

代码实现(清单文件中配置):

<application android:name="PhoneApp"
    android:persistent="true"
    android:label="@string/dialerIconLabel"
    android:icon="@drawable/ic_launcher_phone">

保活程度:一般情况下不被杀,会被 force stop 杀死
PS:该方法需要系统签名

4.覆写Service的onDestroy方法

思路:在onDestroy中再次启动该服务
保活程度:很弱,只在两种情况下work:正在运行里杀服务、DDMS里stop进程
代码实现:

@Override
public void onDestroy() {
    Intent intent = new Intent(this, KeeLiveService.class);
    startService(intent);
    super.onDestroy();
}

5.监听一堆系统静态广播

思路:在发生特定系统事件时,系统会发出响应的广播,通过在 AndroidManifest 中“静态”注册对应的广播监听器,即可在发生响应事件时拉活。
保活强度:我们可以发现,这个方法都是监听系统的一些广播,所以我们需要在我们的应用中注册静态广播,但是静态广播又会出现问题,那就是在4.0版本以上,没有启动过的应用或Force-Stop后收不到静态广播,也就是说4.0以后,如果我们应用从未启动过,或者被Force-Stop杀死过,是无法接收到静态广播的。
  如果是两个应用相互拉起,那么在一个应用内可发送带FLAG_INCLUDE_STOPPED_PACKAGES的Intent,那即使另一个应用也是以上两种情况,也可以接收到系统的广播。
应用1的代码实现:

//应用1,发送拉起服务的广播
Intent intent = new Intent();
intent.setAction("com.action.keepLive");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.sendBroadcast(intent);

应用2的代码实现:

//清单文件中配置
<receiver android:name="com.yzy.supercleanmaster.receiver.KeepLiveReceiver">
    <intent-filter>
        <action android:name="com.action.keepLive" />
    </intent-filter>
</receiver>
//源码实现
public class KeepLiveReceiver extends BroadcastReceiver{
    //应用2中,接受应用1发送的广播,进行服务的拉起
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, KeeLiveService.class);
        context.startService(i);
    }
}

6.监听第三方应用的静态广播

思路:通过反编译第三方 Top 应用,如:手机QQ、微信、支付宝、UC浏览器等,以及友盟、信鸽、个推等 SDK,找出它们外发的广播,在应用中进行监听,这样当这些应用发出广播时,就会将我们的应用拉活。
保活强度:
该方案的局限性除与系统广播一样的因素外,主要受如下因素限制:
1) 反编译分析过的第三方应用的多少;
2) 第三方应用的广播属于应用私有,当前版本中有效的广播,在后续版本随时就可能被移除或被改为不外发,这些因素都影响了拉活的效果。

7.AlarmManager唤醒

思路:通过AlarmManager设置一个定时器,定时的唤醒服务
保活强度:killBackgroundProcess下,大部分情况work,
不敌force-stop,闹钟会被清除。

8.账户同步,定时唤醒

思路:android系统里有一个账户系统,系统定期唤醒账号更新服务,同步的事件间隔是有限制的,最短1分钟。
难点:需要手动设置账户,你如何骗你的用户给你手动设置账户完了之后不卸载你,必须联网。
保活强度: 该方案适用于所有的 Android 版本,包括被 forestop 掉的进程也可以进行拉活。最新 Android 版本(Android N)中系统好像对账户同步这里做了变动,该方法不再有效。

9.1像素悬浮层

思路:1像素悬浮层是传说的QQ黑科技,监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。
保活强度: 前台进程,跟前台服务差不多。需要权限,可以被force-stop杀死。有些手机系统禁止一像素保活,如魅族手机。

10.应用间互相拉起

思路:app之间知道包名就可以相互唤醒了,比如你杀了我qq,只要微信还在就能确保随时唤醒qq。还有百度全系app都通过bdshare实现互拉互保,自定义一个广播,定时发,其他app收广播自起等。

11.心跳唤醒

思路:微信保活技术,依赖系统特性:长连接网络回包机制。
保活强度:不敌force-stop,需要网络,API level >= 23的doze模式会关闭所有的网络。

12.双进程保活策略

思路:在应用被打开的时候,启动两个后台服务,这两个后台服务是相互依存的,也就是说当一个进程被干掉的时候,另一个存活的进程就立马将其拉起唤醒,也就是打一个时间差。

在一个服务断开连接的时候开启另一个服务,示例代码如下:

class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "建立连接成功!");
            
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "LocalService服务被干掉了~~~~断开连接!");
            Toast.makeText(RemoteService.this, "断开连接", 0).show();
            //启动被干掉的
            RemoteService.this.startService(new Intent(RemoteService.this, LocalService.class));
            RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT);
        }
        
    }

改进思路:利用JobService保证在息屏后,CPU进入休眠状态时如果服务没有在工作,则进行唤醒。示例代码如下:

public class JobHandleService extends JobService{
    private final String TAG = "JobHandleService";
    private int kJobId = 0;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "jobService create");
        
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "jobService start");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }
    
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    }
    
    @Override
    public boolean onStartJob(JobParameters params) {
        // TODO Auto-generated method stub
        Log.i(TAG, "job start");
//      scheduleJob(getJobInfo());
        boolean isLocalServiceWork = isServiceWork(this, 你的本地服务ref----XXXX.LocalService);
        boolean isRemoteServiceWork = isServiceWork(this, 你的远程服务ref----XXXX.RemoteService);
//      Log.i(TAG, "localSericeWork:"+isLocalServiceWork);
//      Log.i(TAG, "remoteSericeWork:"+isRemoteServiceWork);
        if(!isLocalServiceWork||
           !isRemoteServiceWork){
            this.startService(new Intent(this,LocalService.class));
            this.startService(new Intent(this,RemoteService.class));
            Toast.makeText(this, "process start", Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i(TAG, "job stop");
//      Toast.makeText(this, "process stop", Toast.LENGTH_SHORT).show();
        scheduleJob(getJobInfo());
        return true;
    }

    /** Send job to the JobScheduler. */
    public void scheduleJob(JobInfo t) {
        Log.i(TAG, "Scheduling job");
        JobScheduler tm =
                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        tm.schedule(t);
    }
    
    public JobInfo getJobInfo(){
        JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, JobHandleService.class));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setPersisted(true);
        builder.setRequiresCharging(false);
        builder.setRequiresDeviceIdle(false);
        builder.setPeriodic(10);//间隔时间--周期
        return builder.build();
    }
    
    
    /** 
     * 判断某个服务是否正在运行的方法 
     *  
     * @param mContext 
     * @param serviceName 
     *            是包名+服务的类名(例如:net.loonggg.testbackstage.TestService) 
     * @return true代表正在运行,false代表服务没有正在运行 
     */  
    public boolean isServiceWork(Context mContext, String serviceName) {  
        boolean isWork = false;  
        ActivityManager myAM = (ActivityManager) mContext  
                .getSystemService(Context.ACTIVITY_SERVICE);  
        List<RunningServiceInfo> myList = myAM.getRunningServices(100);  
        if (myList.size() <= 0) {  
            return false;  
        }  
        for (int i = 0; i < myList.size(); i++) {  
            String mName = myList.get(i).service.getClassName().toString();  
            if (mName.equals(serviceName)) {  
                isWork = true;  
                break;  
            }  
        }  
        return isWork;  
    }  
}

13.音乐静音播放保活

思路:在保活服务中开启一个MediaPlayer,循环播放静音文件。服务被杀的时候在onDestroy中重启服务。示例代码如下:

public class ResidentService extends Service {
    private final static String TAG = "ResidentService";
    private MediaPlayer mMediaPlayer;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
        if (mMediaPlayer != null) {
            mMediaPlayer.setLooping(true);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 服务启动后开始播放静音文件
                startPlayMusic();
            }
        }).start();
        return START_STICKY;
    }

    private void startPlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.start();
        }
    }

    private void pausePlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
        }
    }

    private void stopPlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 服务被杀掉时,停止播放静音文件,并重启保活服务
        stopPlayMusic(); 
        Intent intent = new Intent(getApplicationContext(), GameResidentService.class);
        startService(intent);
    }
}

14.Native进程拉起

思路:开启native子进程,定时发intent。
保活强度:单杀可以杀死,force close 5.0以上无效,5.0以下部分手机无效,第三方软件下无效,且无法保证实时常驻。该策略建立在保证c进程不挂的基础上,才能轮询,但是就目前来看,只有5.0以下的非国产机才会有这样的漏洞。也就是说在force close的时候,系统忽略c进程的存在,5.0以上包括5.0的哪怕源生系统也会连同c进程一起清理掉,国产机就更不用说了。即使这样,在5.0以下的非国产机上,如果安装了获取root权限的360\cm的话,也是可以直接清理掉,也就是说会失效。
  Native进程守护缺点非常明显,那就是守护是单向的,也就是说只能a保b,b保不了a;a保b也不是在b死了立刻拉起来,要等到了时间才会去拉。

15.总结

Android应用的保活策略很多,每种方式都有其优缺点和实用范围,具体采用哪一种保活策略,需要结合应用的类型,如是系统应用还是第三方独立应用,也要结合应用的保活需求,如什么场景下保活、保活多久等等。有很多时候一种保活策略往往不能达到很好的保活效果,可以考虑结合几种保活策略。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值