如何让进程杀不死(3) (小总结)

根据上面几篇博客我们懂得android回收进程的策略:
今天就开始想一下,在此基础上,如何实现保活,当然作为一个android开发,最先想到的肯定是在framework层有没有什么机制可以利用实现保活,当时整理了以下几点:
1、将Service设置为前台进程

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

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

4、覆写Service的onDestroy方法

5、添加广播监听android.intent.action.USER_PRESENT事件以及其他一些可以允许的事件

6、服务互相绑定

7、设置闹钟,定时唤醒

8、账户同步,定时唤醒

9、native层保活

那么我们仔细点分析各种的做法吧:
1、将Service设置为前台进程
本质是修改了Service所在进程的进程优先级,有了前台进程的优先级,在android系统清理内存的时候,他被杀死的优先级仅高于前台的activity,也就是正在和用户交互的页面,而且使用ddms杀进程他也可以自己启动起来。
但是在api 17以上,设置了一个前台服务,他会以一个无法消除的notification的样式出现在用户的手机状态栏里,大大降低了用户体验。
那么如何去隐藏这个notification?

public class DaemonService extends Service {
    @Override
    public void onCreate() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(250, builder.build());
        } else {
            startForeground(250, new Notification());
        }
    }
}

值得注意的是在Android 4.3以前我们可以通过构造一个空的Notification,这时通知栏并不会显示我们发送的Notification,但是自从4.3以后谷歌似乎意识到了这个问题,太多流氓应用通过此方法强制让自身悄无声息置为前台,于是从4.3开始谷歌不再允许构造空的Notification,如果你想将应用置为前台那么请发送一个可见的Notification以告知用户你的应用进程依然在后台运行,这么就比较恶心了,本来我的进程是想后台龌龊地运行,这下非要让老子暴露出来,因此我们得想办法将这个Notification给干掉。上面的代码中我们在发送Notification的时候给了其一个唯一ID,那么问题来了,假设我启动另一个Service同时也让其发送一个Notification使自己置为前台,并且这个Notification的标志值也跟上面的一样,然后再把它取消掉再停止掉这个Service的前台显示会怎样呢:

public class DaemonService extends Service {
    private static boolean sPower = true;

    @Override
    public void onCreate() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(250, builder.build());
            startService(new Intent(this, CancelService.class));
        } else {
            startForeground(250, new Notification());
        }
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (sPower) {
                    if (System.currentTimeMillis() >= 123456789000000L) {
                        sPower = false;
                    }
                    SystemClock.sleep(3000);
                }
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }
}
public class CancelService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.mipmap.ic_launcher);
        startForeground(250, builder.build());
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(1000);
                stopForeground(true);
                NotificationManager manager =
                        (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                manager.cancel(250);
                stopSelf();
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }
}

如上代码所示,我们先在DaemonService中发送一个Notification并将其置为前台,而后如果是4.3及其以上的版本的话我们就start另外一个CancelService,这个CancelService的逻辑很简单,发送与DaemonService中ID相同的Notification然后将其取消并取消自己的前台显示,然后停止,大家看到这里可能觉得很奇葩,其实我们就是自导自演装了一次逼。其实就是个小技巧而已,虽然我们通过CancelService干掉了前台显示需要的Notification,但是,请大家查看一下当前进程的adj值,你就会发现,我们DaemonService所在的进程竟然还是可见进程!

2、在service的onstart方法里返回 STATR_STICK
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

START_STICKY:系统就会重新创建这个服务并且调用onStartCommand()方法,但是它不会重新传递最后的Intent对象,这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。

START_NOT_STICKY:直到接受到新的Intent对象,才会被重新创建。这是最安全的,用来避免在不需要的时候运行你的服务。

START_REDELIVER_INTENT:系统就会重新创建了这个服务,并且用最后的Intent对象调。等待中的Intent对象会依次被发送。这适用于如下载文件。

3、添加Manifest文件属性值为android:persistent=“true”
如果你的应用能设置这个属性,可以全文跳过我这个系列所有文章。因为他真的可以杀不死,像系统的keyguard进程,media进程,且这些进程的adj都是负数,代表了前台activity黑屏了他们也不会死。但是这个属性需要系统shareuid,然后编译不过,因为需要系统签名,什么?你用系统签名?请忽略我全部
所以然并卵。

4、覆写Service的onDestroy方法
你会说这么low也行?为了把我知道的所有方式都列举出来,所以这里也说一下,在设置里面的正在运行,注意是正在运行里面,点击关闭,会走onDestroy回调方法,你在这里可以把自己启动起来。
但是仍然然并卵,设置里面的force close才是真正的劲敌,还有root了的360,cm这些boos才是我们要解决的对象。

5、添加广播监听android.intent.action.USER_PRESENT事件以及其他一些可以允许的事件
这个有必要说一下,这个广播相信可能有的朋友不是很清楚,这是一个android解锁的广播事件。
注意:

1.这不是SCREEN_ON\SCREEN_OFF广播,,这不是SCREEN_ON\SCREEN_OFF广播,这不是SCREEN_ON\SCREEN_OFF广播。

2.这是一个可以静态注册的广播,这是一个可以静态注册的广播,这是一个可以静态注册的广播。

所以在manifest里面注册之后不需要任何前提,理论上用户每次开屏解锁都会触发我们的onReceive事件,在这里我们可以检查进程服务是否在,不在就拉起来。

但是,这个事件只有解锁才有,如果用户的没有设置锁屏,那么这个事件就是没有的,而且我们的目标是保证进程一直存活,而不是尽可能多的活起来。所以这个当作一个补充的手段也不错,另外所有那些可以静态注册的广播都可以这样搞,前提是你不怕用户看到你申请了好多权限,当然这个USER_PRESENT事件是不需要权限的。

以下是几个常见可以静态注册的广播,另外android7取消了wifi的静态广播注册,没有证实

android.intent.action.USER_PRESENT
android.NET.conn.CONNECTIVITY_CHANGE
android.intent.action.MEDIA_MOUNTED
android.intent.action.MEDIA_UNMOUNTED
android.Net.wifi.RSSI_CHANGED
android.net.wifi.STATE_CHANGE
android.net.wifi.WIFI_STATE_CHANGED

6、服务互相绑定
这个是android里面一个特性,跨进程bind一个service之后,如果被bind的service挂掉,bind他的service会把他拉起来。依然然并卵,具体为什么以后再说。
7、设置闹钟,定时唤醒
市面上了解到的大部分应用是用这种保活方式,使用系统闹钟定时发通知过来唤醒进程。
但是,

且先不说高频的唤醒和手机厂商对于wakelock的控制上造成的耗电问题。单单保活效果上就很难过关,force close直接杀掉,没有挣扎的机会,360、cm更是随便杀。说下alarm的几个参数
larmManager.RTC,硬件闹钟,不唤醒手机(也可能是其它设备)休眠;当手机休眠时不发射闹钟。

AlarmManager.RTC_WAKEUP,硬件闹钟,当闹钟发躰时唤醒手机休眠;

AlarmManager.ELAPSED_REALTIME,真实时间流逝闹钟,不唤醒手机休眠;当手机休眠时不发射闹钟。

AlarmManager.ELAPSED_REALTIME_WAKEUP,真实时间流逝闹钟,当闹钟发躰时唤醒手机休眠;

嗯,RTC闹钟和ELAPSED_REALTIME最大的差别就是前者可以通过修改手机时间触发闹钟事件,后者要通过真实时间的流逝,即使在休眠状态,时间也会被计算。

8、账户同步,定时唤醒
这个估计也是好多人不知道一点,android系统里有一个账户系统,设置一个自己的账户,android会定期唤醒账户更新服务,我们可以自己设定同步的事件间隔。且发起更新的是系统,不会受到任何限制。看下效果,晚上下班到第二天早晨,开着cm后台自动清理
log上来看唤醒时间一直正常,且在睡眠中是不会产生唤醒的。

使用方法也很简单:
他的局限性在于:

第一,用户会在系统设置的账户列表里面看到一个不认识的账户;

第二,同步的事件间隔是有限制的,最短1分钟,见源码,如果小雨60秒,置为60秒。而且各种国产机怎么改的源码我们未可知,是不是都能用仍然未可知;

第三,很致命,某些手机比如note3需要手动设置账户,你如何骗你的用户给你手动设置账户完了之后不卸载你;

第四,也很致命,必须联网!google提供这个组件是让你同步账户信息,不联网你同步个鬼,我们要保活,可以不联网不做事,但是不能不联网就死

9、native层保活
终于要到正文了。下一篇开启native保活的篇章,首先上一个github上别人的native保活方案,也是绝大多数公司采用的native方案,笔者所在公司亦是北京知名互联网公司,之前也采取的类似方案,但是,其实只是开了一个native进程定期发intent而已,缺陷是:只能简单保活,360、cm碾压其无压力,但仍有可借鉴之处,下一篇分析,然后开始我的native之旅

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值