Service防杀死

作者: 慕涵盛华 | 来源:发表于2017-08-31 20:19 被阅读402次

    目录

    简介

    首先要弄明确一个问题,就是我们平时说的Service防杀死,其实防止是Service所在的进程被杀死,而不是Service这个组件,因为Android 只杀死进程,而不是组件。
    当我们说进程优先级的时候是以 activity、service 这样的组件来说的,这些组件的优先级是在进程的级别上,不是组件级别上。只要一个组件的状态发生变化,就会影响进程的优先级;比如:启动一个前台服务,那么就会将该服务所在的整个进程变为前台进程。
    弄清楚了这个问题后,下面我们首先了解一下进程的优先级。

    进程优先级

    前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程。

    1.前台进程;Foreground process
    • 用户正在交互的Activity(onResume())
    • 当某个Service绑定正在交互的Activity。
    • 被主动调用为前台Service(startForeground())
    • 组件正在执行生命周期的回调(onCreate()/onStart()/onDestroy())
    • BroadcastReceiver 正在执行onReceive();
    2.可见进程;Visible process
    • 我们的Activity处在onPause()(没有进入onStop())
    • 绑定到前台Activity的Service。
    3.服务进程;Service process

    简单的startService()启动。

    4.后台进程;Background process

    对用户没有直接影响的进程----Activity出于onStop()的时候。
    android:process=":xxx"

    5.空进程; Empty process

    不含有任何的活动的组件。(android设计的,为了第二次启动更快)

    防止进程被杀死

    所谓进程防杀死,就是做到进程尽量不被系统杀死,并不能保证100%存活,因为受到内存,手机厂商的限制等。上面提到进程优先级,优先级越高越不容易被杀死,所以要想防止进程被杀死,就要提高进程的优先级。

    QQ的做法

    QQ采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉,同时把自己的核心服务再开启一次。下面我们就简单模拟一下。

    • 在首页我们放置一个按钮,点击按钮启动1个像素的Activity

      /** 启动一个像素的Activity */
      public void start(View view){
        Intent intent = new Intent(MainActivity.this, LiveActivity.class);
        startActivity(intent);
        finish();
        Toast.makeText(this, "启动完成", Toast.LENGTH_SHORT).show();
      }
      
    • 设置Activity为一个像素,首先我们要将Activity的背景设置为透明,否则显示的是黑色

       <style name="LiveStyle" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowDisablePreview">true</item>
        <item name="android:windowNoDisplay">false</item>
      </style>
      
    • 显示一个像素

      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Window window = getWindow();
        window.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.width = 1; // 1 px
        params.height = 1;
        params.x = 0;
        params.y = 0;
        window.setAttributes(params);
      }
      

    从效果图中看出,我们启动Activity后,手机页面不能滑动,当点击返回键的时候,又能滑动了,说明成功启动了1px 的Activity,下面监听手机锁屏事件。

    封装一个工具类,专门监听锁屏事件

    public class ScreenListener {
    
    private Context mContext;
    private ScreenBroadcastReceiver mScreenReceiver;
    private ScreenStateListener mScreenStateListener;
    
    public ScreenListener(Context context) {
        mContext = context;
        mScreenReceiver = new ScreenBroadcastReceiver();
    }
    
    //监听锁屏事件的广播接受者
    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        private String action = null;
        @Override
        public void onReceive(Context context, Intent intent) {
            action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) {//开屏
                mScreenStateListener.onScreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { //锁屏
                mScreenStateListener.onScreenOff();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) { //解锁
                mScreenStateListener.onUserPresent();
            }
        }
    }
    
    //初始化
    public void init(ScreenStateListener listener) {
        mScreenStateListener = listener;
        registerListener();
        getScreenState();
    }
    
    //根据锁屏状态回调对应的方法
    private void getScreenState() {
        PowerManager manager = (PowerManager) mContext
                .getSystemService(Context.POWER_SERVICE);
        if (manager.isScreenOn()) {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOn();
            }
        } else {
            if (mScreenStateListener != null) {
                mScreenStateListener.onScreenOff();
            }
        }
    }
    
    public void unregisterListener() {
        mContext.unregisterReceiver(mScreenReceiver);
    }
    
    //动态注册锁屏广播
    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mScreenReceiver, filter);
    }
    
    //锁屏事件回调接口
    public interface ScreenStateListener {
         void onScreenOn();
         void onScreenOff();
         void onUserPresent();
    }
    }
    

    启动一个Service专门管理Activity

    public class MyService extends Service{
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        ScreenListener listener = new ScreenListener(this);
        listener.init(new ScreenListener.ScreenStateListener() {
            @Override
            public void onScreenOn() {
                //开屏,销毁1px Activity
                LiveActivityManager.getInstance(MyService.this).finishKeepLiveActivity();
            }
    
            @Override
            public void onScreenOff() {
                //锁屏,启动 1px Activity
                LiveActivityManager.getInstance(MyService.this).startKeepLiveActivity();
            }
    
            @Override
            public void onUserPresent() {  //解锁    }
        });
    }
    }
    

    在应用启动的时候启动该服务

    Intent intent = new Intent(this, MyService.class);
    startService(intent);
    

    添加权限

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
    

    通过日志的输出可以看出,在锁屏的时候调用了onCreate(),在亮屏的时候调用了onDestroy();

    上面只是简单的模拟了一下,但是实现上述功能还有一个前提就是MyService这个服务得一直存活,不然没办法监听处理了。

    双进程守护

    双进程守护,可以防止单个进程杀死,同时可以防止第三方的软件清理掉。一个进程被杀死,另外一个进程又被他启动。相互监听启动,因为杀进程是一个一个杀的。本质是和杀进程时间赛跑。

    • 1.创建两个Service,实现互相监听

      <service android:name=".LocalService"/>
      <service
           android:name=".RemoteService"
           android:process=":remote"/> // 开启一个新的进程
      

    LocalService和RemoteService位于不同的进程中,实现互相的监听,他们的代码一样,只不过是监听的对象不一样,所以下面只贴出了一个文件的代码。

    LocalService.java

    public class LocalService extends Service {
    
    private MyConnection connection;
    private MyBinder binder;
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        connection = new MyConnection();
        binder = new MyBinder();
    }
    
    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        //启动的时候绑定RemoteService
        LocalService.this.bindService(new Intent(LocalService.this,RemoteService.class),connection,
                Context.BIND_IMPORTANT);
        return START_STICKY;
    }
    
    // 绑定RemoteService建立的链接
    class MyConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("service","绑定RemoteService");
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //RemoteService被杀死,重新启动绑定
            Intent service = new Intent(LocalService.this,RemoteService.class);
            LocalService.this.startService(service);
            LocalService.this.bindService(service,connection, Context.BIND_IMPORTANT);
        }
    }
    
     //LocalService和RemoteService通信的Binder
    class MyBinder extends RemoteInterface.Stub{
    
        @Override
        public String getServicName() throws RemoteException {
            return "LocalService";
        }
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
       //销毁的时候要解绑
        unbindService(connection);
    }
    }
    

    从上述代码中可以看出,在LocalService一启动的时候(onStartCommand),去绑定RemoteService,对应的连接为connection,那么当RemoteService被解绑的时候就会调用onServiceDisconnected方法,在该方法只能再次启动和绑定RemoteService。同理在RemoteService中也是一样的逻辑,这样就能实现两个服务互相监听,一个被杀死,另一个立马再次启动它。

    从效果图中看出,我们关闭一个服务,又会被立刻启动起来,通过右边启动的时间也可以看出来,如果想提高进程的优先级,可以在onStartCommand()方法中调用 startForeground()将进程提升为前台进程。这样虽然能实现进程常驻,但是两个服务一直在后台运行,是非常耗费资源的,尤其是电量。那么有没有更好一点的办法呢?那就是下面要说的内容了。

    Jobscheduler

    Android 5.0系统以后,Google为了优化Android系统,提高使用流畅度以及延长电池续航,加入了在应用后台/锁屏时,系统会回收应用,同时自动销毁应用拉起的Service的机制。同时为了满足在特定条件下需要执行某些任务的需求,google在全新一代操作系统上,采取了Job (jobservice & JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源。这样又能满足APP开发的要求,又能满足系统性能的要求。

    实现进程防杀死

    把任务加到系统调度队列中,当到达任务窗口期的时候就会执行,我们可以在这个任务里面启动我们的进程。这样可以做到将近杀不死的进程。

    具体实现

    • 派生JobService 子类,定义需要执行的任务
    • 从Context 中获取JobScheduler 实例
    • 构建JobInfo 实例,指定 JobService任务实现类及其执行条件
    • 通过JobScheduler 实例加入到任务队列
    @SuppressLint("NewApi")
    public class JobHandleService extends JobService{
    private int kJobId = 0;
    @Override
    public void onCreate() {
        super.onCreate();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }
    
    @Override
    public boolean onStartJob(JobParameters params) {
        boolean isLocalServiceWork = isServiceWork(this, "com.gfd.demo.LocalService");
        boolean isRemoteServiceWork = isServiceWork(this, "com.gfd.demo.RemoteService");
        if(!isLocalServiceWork||
           !isRemoteServiceWork){
            this.startService(new Intent(this,LocalService.class));
            this.startService(new Intent(this,RemoteService.class));
        }
        return true;
    }
    
    @Override
    public boolean onStopJob(JobParameters params) {
        scheduleJob(getJobInfo());
        return true;
    }
    
    public void scheduleJob(JobInfo t) {
        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);
    }
    
    //判断Service是否在存活
    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;  
    }  
    }
    

    关注微信公众号获取更多相关资源

    Android小先生

    相关文章

      网友评论

      • b707c3937ef8:好像有时候监听不到开关锁屏的广播,如果没把1像素的activity关闭掉,那么你进去的时候屏幕都划不动的,用户完全懵逼,只能重启手机了
      • 9cdc0bad580e:您好 1像素可以做到么(貌似不太行了) 我原来试了好多方法 总是不能完全保证
        9cdc0bad580e: @慕涵盛华 不好意思 我才刚刚学安卓 可能了解的不够多 最近要做比赛 没有这种保活的硬性要求 可能最近不能再探讨这个问题 不过希望博主给一个完整的demo先保存一下 万分感谢
        9cdc0bad580e: @慕涵盛华 oppo手机貌似不太友好 他管理进程服务貌似不太一样 博主有兴趣可以试一下quantitytime这个app 给了root华为不可以杀死他 但是oppo依然可以 貌似不存在完全保活😄
        慕涵盛华:@林妹妹的妹妹 我这实现了,你的效果是什么?怎么实现的?
      • 老年追梦人:收藏在看:heart_eyes:
        慕涵盛华:@老年追梦人哈哈,够投入的啊
        老年追梦人:@慕涵盛华 地铁上看最棒了。一不小心就到站了:sunglasses:
        慕涵盛华: @老年追梦人 一起学习
      • Jlanglang:少些这玩意,安卓也不会一直被人说永久了卡了
        慕涵盛华: @Jlanglang 根据需求,权衡吧!

      本文标题:Service防杀死

      本文链接:https://www.haomeiwen.com/subject/ehfqjxtx.html