汇总Service保活方案

某呼叫器App用到的

Posted by Toeii on September 9, 2018

前言

最近利用多余时间接了一个呼叫器的工具类app单子,其中为了实现锁屏呼叫功能,采用了多种方式保活后台服务相结合从而实现该功能。由于不同的手机,不同的Android版本保活效果各有差异,所以在这里总结记录分享一下。

普通方案

1,利用onStartCommand重启(提高存活率)

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // TODO Auto-generated method stub  
        return START_STICKY;  
        //return super.onStartCommand(intent, flags, startId);  
    } 

2,利用onDestory里面重启服务(提高存活率)

    @Override
    public void onDestroy() {
       Intent intent = new Intent(this, AliveService.class);
            intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,
                    "START_FORM_SELF");
      startService(intent);
    }

3,manifest中配置优先级(提高存活率)

    <service
            android:name=".MyService"
            android:permission="true"
            android:process="com.android.toeii.service">
    </service>

4,利用前台化,提高进程优先级

4.1,利用Notification实现前台服务(提高存活率,需要考虑Notification兼容性)

    public void onCreate() {
        Notification notification = new Notification(); 
        startForeground(1, notification);
    }

4.2,监听熄屏创建1px桌面级窗口占用前台进程(提高存活率)

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置1像素
        Window window = getWindow();
        window.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x = 0;
        params.y = 0;
        params.height = 1;
        params.width = 1;
        window.setAttributes(params);
        //结束该页面的广播
        endReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                finish();
            }
        };
        registerReceiver(endReceiver, new IntentFilter("finish"));
        //检查屏幕状态
        checkScreen();
    }

5,进程双开,相互唤醒 (拥有自启动权限时唤醒率很高,没权限则Android 5.0之后失效)

开启一个新线程,绑定Service之后,利用IPC唤醒。 具体实现可以参考这篇文章

另外思路,启动看门狗服务进行进程双开监控。这种方式比普通IPC唤醒更高效。

6,利用系统闹钟和定时器相关手段唤醒

6.1 AlarmManager(Android 6.0之后失效)

    public void sendBroadcastReceiver(){  
                Intent intent = new Intent(AlarmController.this,
                        RepeatingAlarm.class);
                PendingIntent sender = PendingIntent.getBroadcast(
                        AlarmController.this, 0, intent, 0);
                Calendar calendar = Calendar.getInstance();
                calendar.setTimeInMillis(System.currentTimeMillis());
                calendar.add(Calendar.SECOND, 10);
                AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
                am.setRepeating(
                    AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(), 10 * 1000, sender);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (PushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())){
            Log.i("tag","闹钟响了");
            Intent intent = new Intent(this, MyService.class);
            intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,"START_FORM_SELF");
            startService(intent);
        }
    }
         

6.2 利用JobScheduler实现JobService(拥有自启动权限时唤醒率很高,没权限则Android 7.0之后失效)

//注册JobScheduler调度任务后...
public class JobProtectService extends JobService {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent,flags,startId);
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Intent intent = new Intent(this, MyService.class);
        intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,
            "START_FORM_SELF");
        startService(intent);
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

进阶方案

1,通过监听系统级广播唤醒(Android 7.0之后失效)

2,通过Push推送唤醒(这里推荐厂商自家的推送,第三方推送到达率相比之下低一点)

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        if (PushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
            String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
        } else if (PushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
             Intent intent = new Intent(this, MyService.class);
             intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,
                    "START_FORM_SELF");
             startService(intent);
        } 
    }

3,利用GPS机制唤醒 (系统自带或者第三方地图SDK都可以尝试)

        public static void formListenerGetLocation(){  
            locationManager = (LocationManager)mActivity.getSystemService(Context.LOCATION_SERVICE);  
            locationListener = new LocationListener() {  
                @Override  
                public void onLocationChanged(Location location) {  
                    //位置信息变化时触发  
                    //这里检查Service是否需要唤醒(是否存活),然后进行唤醒。
                    checkService...
                    Intent intent = new Intent(this, MyService.class);
                    intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,
                            "START_FORM_SELF");
                    startService(intent);
                }  

                @Override  
                public void onStatusChanged(String provider, int status,Bundle extras) {  
                    //GPS状态变化时触发  
                }  

                @Override  
                public void onProviderEnabled(String provider) {  
                    
                }  

                @Override  
                public void onProviderDisabled(String provider) {  
                   
                }  
            };  
            
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);  
        }

4,IM呼入方式唤醒

//收到IM呼入信息后...
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        if (IMInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
             Intent intent = new Intent(this, MyService.class);
             intent.putExtra(RecorderService.SERVICE_FIRST_START_KEY,
                    "START_FORM_SELF");
             startService(intent);
        } 
    }

其他方案

1,利用LowMemoryKiller重启应用唤醒

Android的Low Memory Killer基于Linux的OOM机制,在Linux中,内存是以页面为单位分配的,当申请页面分配时如果内存不足会通过以下流程选择bad进程来杀掉从而释放内存。

    alloc_pages -> out_of_memory() -> select_bad_process() -> badness()

在Low Memory Killer中通过进程的oom_adj与占用内存的大小决定要杀死的进程,oom_adj越小越不容易被杀死。

2,添加为自定义系统服务

定义为系统服务之后,可以通过WindowManager调用。 具体实现可以参考这篇文章

3,利用系统漏洞唤醒

至此,保活方式总结完毕,可能还有更多我所不知道的黑科技方式实现吧,未来可能会一一挖掘和尝试。