应用进程防杀死

1.应用程序的优先级

为了提高用户的体验,安卓系统采用了一让应用“尽可能”保留在内存中的机制,这样有助于下次启动时减少加载应用的时间。

但是这种设计导致的问题就是内存常常处于吃紧的状态,为此安卓系统定义了5个进程等级,当内存不足的情况下按照5个优先级的不同来决定到底先杀哪个进程。

如下就是应用的进程等级:

这里写图片描述

  1. 前台进程;Foreground process

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

  3. 服务进程;Service process,简单的startService()启动。

  4. 后台进程;Background process,对用户没有直接影响的进程—-Activity出于onStop()的时候。
  5. 空进程; Empty process,不含有任何的活动的组件。(android设计的,为了第二次启动更快,采取的一个权衡)

2.如何提升应用的优先级

上面我们介绍了5个等级优先级,那么如何让你的应用长期驻留在内存中呢,我们可以让应用提高他所在的等级。

比如,在手机屏幕锁屏的时候,经常会将我们的APP杀死(为了省电)。QQ采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉(顺便同时把自己的核心服务再开启一次)。

这里写图片描述

开发步骤:

  1. 创建一个Activity:

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //1.去掉布局
        //setContentView(R.layout.activity_keep_alive);
    
        //2.设置界面只有一个像素
        Window window = getWindow();
        window.setGravity(Gravity.LEFT|Gravity.TOP);
        WindowManager.LayoutParams params=window.getAttributes();
        params.width=1;
        params.height=1;
        window.setAttributes(params);
    
        //保存当前创建的Activity以便我们在屏幕解锁后杀掉
        ActivityManager.getInstance().setKeepAliveActivity(this);
        Log.i("IT520","KeepAliveActivity onCreate()");
    }
    
  2. 当前的Activity在启动的时候会产生黑屏,主要是我们没有设置好该界面的样式,接下来我们需要创建样式并应用在AndroidManifest.xml中:

    <style name="keppalive_style">
        <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>
    
  3. 启动服务在后台监听屏幕的锁屏解锁状态,服务里面创建了一个我们自定义的监听器(看第4步),可以帮助我们在锁屏的时候启动1px界面,并在解锁后关闭1px界面:

    public class KeepAliveService extends Service {
    
        private ScreenListener mScreenListener;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mScreenListener = new ScreenListener(this.getApplicationContext());
            mScreenListener.begin(new ScreenListener.ScreenStateListener() {
                @Override
                public void onScreenOn() {//解锁
                    //关闭界面
                    ActivityManager.getInstance().finishActivity();
                }
    
                @Override
                public void onScreenOff() {//锁屏
                    //启动界面
                    ActivityManager.getInstance().startActivity(KeepAliveService.this);
                }
    
                @Override
                public void onUserPresent() {
    
                }
            });
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            throw null;
        }
    }
    
  4. 监听器内部实现了一个屏幕广播接收者,并在调用begin()时通过动态注册广播接收者来达到监听目的:

    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 begin(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 {
            public void onScreenOn();
    
            public void onScreenOff();
    
            public void onUserPresent();
    }
    

    }

3.其他可行的防应用进程杀死技术

所有的技术都不能百分百的方式应用进程被干掉,但可以大大提升应用的存活率。下面列举了一些其他可行的方案:

  1. app运营商和手机厂商可能有合作关系—白名单。
  2. 双进程守护—可以防止单个进程杀死,同时可以防止第三方的360清理掉。
  3. JobScheduler把任务加到系统调度队列中,当到达任务窗口期的时候就会执行,我们可以在这个任务里面启动我们的进程。这样可以做到将近杀不死的进程。
  4. 监听系统级广播,然后把自己启动了。
  5. NDK来解决,Native进程来实现双进程守护。

4.双进程服务在Java层的实现

这里写图片描述

技术的原理就是创建一个服务,每个服务拥有自己的一个进程,当一个进程中的服务被杀掉后,另一个进程服务重新启动原进程服务。

该技术能够达到的期望:

  1. 作为前台进程,提升了优先等级。
  2. 防止用户在设置界面杀死某个进程服务和停止应用。
  3. 防止360,鲁大师等手机优化软件杀死进程。

开发步骤:

  1. 创建一个本地服务和远程服务(另一个进程):

    <service android:name=".LocalService" />
    <service android:name=".RemoteService" android:process=":remoteprocess" />
    
  2. 在应用启动的时候启动两个服务:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            startService(new Intent(this,LocalService.class));
            startService(new Intent(this,RemoteService.class));
        }
    }
    
  3. 在服务创建的时候我们要做3件事如下,因为服务是跨进程的服务所以需要用到aidl。这2个服务的代码差不多,这里贴出其中一个:

    • 在创建时绑定另一个进程的服务
    • 在启动后设置当前服务为前台服务
    • 在另一个进程服务被断开时重新启动服务。

public class RemoteService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("IT520","RemoteService onCreate。。。");
        Intent intent=new Intent(this,LocalService.class);
        bindService(intent,new RemoteServiceConnection(),BIND_IMPORTANT);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setContentTitle("重要的远程服务。。。");
        builder.setTicker("重要的远程服务。。。");
        builder.setContentIntent(PendingIntent.getService(this,0,intent,0));
        builder.setAutoCancel(true);
        startForeground(startId,builder.build());
        //如果系统不小心杀掉该服务,系统会稍后尝试重启
        return START_STICKY;
    }

    private class ServiceAgent extends RemoteConnection.Stub{

        @Override
        public String getConnectionName() throws RemoteException {
            return "RemoteService";
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new ServiceAgent();
    }

    private class RemoteServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("IT520","LocalService 开始连接。。。");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i("IT520","LocalService 断开连接。。。");
            //LocalService已经被断开了 这里再次连接
            Intent intent=new Intent(RemoteService.this,LocalService.class);
            startService(intent);
            bindService(intent,new RemoteServiceConnection(),BIND_IMPORTANT);
        }
    }
}

5.利用JobScheduler重启服务

上面的例子在用户设置界面中强制停止后就不会再启动了,不过我们可以通过系统5.0后提供的JobScheduler来重新启动服务。

开发步骤

  1. 创建JobService服务

    /**
     * 该API只能应用在Android5.0之后,如果想在5.0之前实现可以使用AlarmManager(不太理想)
     */
    @SuppressLint("NewApi")
    public class JobHandleService extends JobService{
        private int kJobId = 0;
    
        ...
    
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("INFO", "jobService start");
            scheduleJob(getJobInfo());
            return START_NOT_STICKY;
        }
    
        @Override
        public boolean onStartJob(JobParameters params) {
            //主要的任务在这里写...
            return true;
        }
    
        @Override
        public boolean onStopJob(JobParameters params) {
            scheduleJob(getJobInfo());
            return true;
        }
    
        /** Send job to the JobScheduler. */
        public void scheduleJob(JobInfo t) {
            Log.i("INFO", "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);//每10ms执行1次任务 这里真是项目可以将时间长度放大
            return builder.build();
        }
    
    
        /** 
         * check whether service is work
         */
        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;  
        }  
    

    }

  2. 在任务中监听之前创建的2个服务包。

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("INFO", "job start");
        boolean isLocalServiceWork = isServiceWork(this, "com.a520it.keepalivebyservicedemo.LocalService");
        boolean isRemoteServiceWork = isServiceWork(this, "com.a520it.keepalivebyservicedemo.RemoteService");
        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;
    }
    
  3. 配置服务

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    
    <service android:name=".JobHandleService"
        android:permission="android.permission.BIND_JOB_SERVICE" />
    
  4. 示例图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq285016127/article/details/78923791