Android Process Guardian

Keywords: Android Mobile

In android development, service is usually used to handle background tasks. When the system process space is tight, the process recovery will be carried out automatically according to the priority.

Process Priority

1. Front-end process: Foreground process.

  • Activeness (onResume ()) in which users are interacting
  • When a Service binds Activeness that is interacting
  • Initially called as a front-end Service (startForeground())
  • The component is executing callbacks for its lifecycle (onCreate()/onStart()/onDestroy())
  • Broadcast Receiver is executing onReceive()

2. Visible process: Visible process

  • Activity is in onPause() (no onStop())
  • Service bound to front-end Activity

3. Service process: Service process

  • Simple startService() startup

4. Background process

  • Processes that have no direct impact on users - when Activity comes out of onStop()
  • android:process=":xxx"

5. Empty process

  • Components that do not contain any activity. (android designed a trade-off for faster second startup)

Improving process priority

Method 1: Start a 1-pixel activity

1. Create a transparent activity of 1-pixel activity

Window window = getWindow();
window.setGravity(Gravity.LEFT|Gravity.TOP);
LayoutParams params = window.getAttributes();
params.height = 1;
params.width = 1;
params.x = 0;
params.y = 0;   
window.setAttributes(params);


<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>

2. Create broadcasts that listen for opening, unlocking and locking screens, and call back

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)) { // Open screen
                mScreenStateListener.onScreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // Lock screen
                mScreenStateListener.onScreenOff();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // Unlock
                mScreenStateListener.onUserPresent();
            }
        }
    }

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 {// Returns screen status information to caller
        public void onScreenOn();
        public void onScreenOff();
        public void onUserPresent();
    }

3. Create a Service to perform listening tasks

@Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        ScreenListener listener = new ScreenListener(this);
        listener.begin(new ScreenStateListener() {
            @Override
            public void onUserPresent() {
            }
            @Override
            public void onScreenOn() {
                // Open Screen - finish this one-pixel Activity
            }       
            @Override
            public void onScreenOff() {
                // Lock Screen - Start Activeness of a Pixel
            }
        });
    }

Method 2: Dual-process guardianship can prevent a single process from killing, and at the same time prevent a third party from cleaning up 360. One process is killed, and the other is started by him, listening to each other. Killing process is a killing process, the essence of which is a race against killing process time. AIDL is used for cross-process communication.

1. Create a local process, write interfaces and methods

<service 
    android:name="xxxx"
    android:process=":remoteprocess">
</service>

2. Create a Service to communicate with the local process, and then create a Service to listen to each other. The two services code logic is the same.

private MyBinder binder;
    private MyServiceConnection conn;
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        if(binder ==null){
            binder = new MyBinder();
        }
        conn = new MyServiceConnection();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
        PendingIntent contentIntent = PendingIntent.getService(this, 0, intent, 0);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setTicker("360")
        .setContentIntent(contentIntent)
        .setContentTitle("I am 360. Who am I afraid of?!")
        .setAutoCancel(true)
        .setContentText("hehehe")
        .setWhen( System.currentTimeMillis());
        //Set the service to run in the front desk to avoid the mobile phone system automatically killing and changing the service.
        startForeground(startId, builder.build());
        return START_STICKY;
    }


    class MyBinder extends RemoteConnection.Stub{
        @Override
        public String getProcessName() throws RemoteException {
            return "LocalService";
        }
    }

    class MyServiceConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "Connect successfully!");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "RemoteService The service was destroyed.~~~~Disconnect!");
            Toast.makeText(LocalService.this, "Disconnect", 0).show();
            //The startup was destroyed
            LocalService.this.startService(new Intent(LocalService.this, RemoteService.class));
            LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
        }
    }

3. Finally, another terrible thing is to use Job Scheduler every few seconds to judge whether the service is running or not, and start the service without it, so that it can hardly kill.

private int kJobId = 0;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("INFO", "jobService create");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("INFO", "jobService start");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        // TODO Auto-generated method stub
        Log.i("INFO", "job start");
//      scheduleJob(getJobInfo());
        boolean isLocalServiceWork = isServiceWork(this, "com.dn.keepliveprocess.LocalService");
        boolean isRemoteServiceWork = isServiceWork(this, "com.dn.keepliveprocess.RemoteService");
//      Log.i("INFO", "localSericeWork:"+isLocalServiceWork);
//      Log.i("INFO", "remoteSericeWork:"+isRemoteServiceWork);
        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;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i("INFO", "job stop");
//      Toast.makeText(this, "process stop", Toast.LENGTH_SHORT).show();
        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);//Interval time - period
        return builder.build();
    }


    /** 
     * A method to determine whether a service is running 
     *  
     * @param mContext 
     * @param serviceName 
     *            Is the class name of the package + service (e.g. net.loonggg.testbackstage.TestService) 
     * @return true The representative is running, and the false representative service is not running 
     */  
    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;  
    }  

Posted by mohvmark on Mon, 08 Apr 2019 18:39:30 -0700