Service防杀死

目录

简介

首先要弄明确一个问题,就是我们平时说的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;  
}  
}

vip视频

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单...
    晨起清风阅读 1,772评论 0 5
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,515评论 25 707
  • 【Android Service】 Service 简介(★★★) 很多情况下,一些与用户很少需要产生交互的应用程...
    Rtia阅读 3,141评论 1 21
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 3,023评论 0 8
  • 01 那是一个阳光灿烂的日子,三尺讲台上隐约可见树影婆娑,斑驳了少女十五岁的雨季。 一个身穿白色运动套装的身躯像光...
    爱上世界的张大路阅读 971评论 5 14