Do Android to keep alive the background

In a previous project, I needed to keep the process alive in the background and not be killed, so BAIDU struggled to find it. Originally, I wanted to rely on Service to keep the process alive. Because my mobile phone runs on the 6.0 system, all the ways I tried to rely on Service ended in failure. So I decided to stand on another Angle to solve the problem — >Android process.

Solution 1: Dual-process daemon

In fact, the principle of products like 360 antivirus software is to clean up the software by traversing the process one by one and killing it if it survives, so we can get our own process and create new processes. Cross-process communication can be achieved through AIDL’s interface, so using dual processes and communication between processes is a feasible solution. Therefore, the first solution is to solve the Android application through the dual process daemon.

The first is an AIDL interface. Services on both sides implement AIDL methods by inherits service_1.stub, with an empty implementation for process communication. The interfaces are declared as follows:


package com.ph.myservice;


interface Service_1 {
    String getName();
}
Copy the code

To maintain the connection, write an internal class that implements the ServiceConnection interface. When the external process kills one of the services, it will enter onDisConnection. Then all it needs to do is start and bind the other process. Since the Service can be started more than once, this is fine, as follows:

package com.ph.myservice;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast;

import java.util.List;

public class LocalService extends Service {
    private ServiceConnection conn;
    private MyService myService;

    @Override
    public IBinder onBind(Intent intent) {
        return myService;
    }


    @Override
    public void onCreate() {
        super.onCreate();
        init();

    }

    private void init() {
        if (conn == null) {
            conn = new MyServiceConnection();
        }
        myService = new MyService();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(), "Local process start", Toast.LENGTH_LONG).show();
        Intent intents = new Intent();
        intents.setClass(this, RemoteService.class);
        bindService(intents, conn, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

    class MyService extends Service_1.Stub {


        @Override
        public String getName(a)throws RemoteException {
            return null;
        }
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("Get connection");

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(LocalService.this."Remote connection is dead.", Toast.LENGTH_SHORT).show();
            LocalService.this.startService(new Intent(LocalService.this,
                    RemoteService.class));
            LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT); }}}Copy the code

The remote services are as follows:

package com.ph.myservice;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast;

public class RemoteService extends Service {
    private MyBinder binder;
    private ServiceConnection conn;

    @Override
    public void onCreate() {
        super.onCreate();
        // system.out. println(" remote process started ");
        init();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(), "Remote Process start", Toast.LENGTH_LONG).show();
        Intent intents = new Intent();
        intents.setClass(this, LocalService.class);
        bindService(intents, conn, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

    private void init() {
        if (conn == null) {
            conn = new MyConnection();
        }
        binder = new MyBinder();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    static class MyBinder extends Service_1.Stub {


        @Override
        public String getName(a)throws RemoteException {
            return "Remote connection";
        }
    }

    class MyConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("Get remote connection");
        }

        @Override
        public void onServiceDisconnected(ComponentName nme) {
            Toast.makeText(RemoteService.this."Local connection is dead.", Toast.LENGTH_SHORT).show();
            RemoteService.this.startService(new Intent(RemoteService.this,
                    LocalService.class));
            RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT); }}}Copy the code

Put a declaration in the layout file

<service android:name=".LocalService" />
        <service
            android:name=".RemoteService"
            android:process=":remote" />Copy the code

As a matter of fact, in my personal test, there is no problem on the emulator below 5.0. No matter how many times it is killed from the system process, TOS will still be restarted. However, the method above 5.0 is invalid. Invalidate this two-process approach to keeping your application alive. Therefore, for above 5.0, we adopt another scheme.

Solution 2 :JobScheduler performs task scheduling and ensures scheduling

JobScheduler class is a new API released by Google in version 21. We can see the following functions of this class from its documentation:

The framework will be smart when you receive your callback, and try batch and delay it as long as possible. Usually if you do not specify a period during your work, it can run at any time according to the job scheduler's current state of the internal queue, however it may be delayed as long as the next time the device is connected to a power supply.Copy the code

This task is executed in the equipment idle period, and system design of this API is not electricity, original intention is to perform some task scheduling, but we imagine, if use this class to carry out our open double process, also should be executed in the equipment idle period, so we wrote a class inherits JobService, In onstart, declare the creation of the JobScheduler object, and set it to execute once as often as possible and start the process automatically. In this way, we can ensure that the process is in the pause state in time and can restart the process. Therefore, we can determine whether our process is recovered in the onStopJob method of JobService. Restart the process if it has been reclaimed. In this way, the process above 5.0 can be saved. The specific code is as follows:

package com.ph.myservice;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.widget.Toast;

import java.util.List;

/** * Created by 86119 on 2017/1/6. */

@SuppressLint("NewApi")
public class JobHandlerService extends JobService {
    private JobScheduler mJobScheduler;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("Service created");

// startService(new Intent(this, LocalService.class));
// startService(new Intent(this, RemoteService.class));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
            JobInfo.Builder builder = new JobInfo.Builder(startId++,
                    new ComponentName(getPackageName(), JobHandlerService.class.getName()));

            builder.setPeriodic(5000); // Run every 5 seconds
            builder.setRequiresCharging(true);
            builder.setPersisted(true);  // Set whether to perform tasks again after the device is restarted
            builder.setRequiresDeviceIdle(true);

            if (mJobScheduler.schedule(builder.build()) <= 0) {
                //If something goes wrong
                System.out.println("Job failure");
            } else {
                System.out.println("Work well"); }}return START_STICKY;
    }


    @Override
    public boolean onStartJob(JobParameters params) {

        Toast.makeText(this."Service startup", Toast.LENGTH_SHORT).show();
// || isServiceRunning(this, "com.ph.myservice.RemoteService") == false
        System.out.println("Get to work");
// if (! isServiceRunning(getApplicationContext(), "com.ph.myservice") || ! isServiceRunning(getApplicationContext(), "com.ph.myservice:remote")) {
// startService(new Intent(this, LocalService.class));
// startService(new Intent(this, RemoteService.class));
/ /}

       /* boolean serviceRunning = isServiceRunning(getApplicationContext(), "com.ph.myservice"); System.out.println(" process 1 "+ serviceRunning); boolean serviceRunning2 = isServiceRunning(getApplicationContext(), "com.ph.myservice:remote"); System.out.println(" process 2 "+ serviceRunning2); * /
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        if(! isServiceRunning(this."com.ph.myservice.LocalService") | |! isServiceRunning(this."com.ph.myservice.RemoteService")) {
            startService(new Intent(this, LocalService.class));
            startService(new Intent(this, RemoteService.class));
        }
        return false;
    }

    // Whether the service is running
    public boolean isServiceRunning(Context context, String serviceName) {
        boolean isRunning = false;
        ActivityManager am = (ActivityManager) this
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses();


        for (ActivityManager.RunningAppProcessInfo info : lists) {// Get the run service and start again
            System.out.println(info.processName);
            if (info.processName.equals(serviceName)) {
                isRunning = true; }}returnisRunning; }}Copy the code
package com.ph.myservice;

import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            openJobService();
        } else{ openTwoService(); }}private void openTwoService() {
        startService(new Intent(this, LocalService.class));
        startService(new Intent(this, RemoteService.class));
    }

    private void openJobService() {

        Intent intent = new Intent();
        intent.setClass(MainActivity.this, JobHandlerService.class); startService(intent); }}Copy the code

There is no problem in the huawei real machine test of my 6.0 system. Even in the resting state, the process is still alive. No matter how long it takes to open the screen, TOS will still be displayed. These are my two ways to keep Android alive, and there are several more, but I haven’t done it yet, so I won’t talk about it here.