Unkillable service-Java Layer Implementation
introduce
To begin with, let's talk about how we can achieve this:
1. Improving process priority
2.java Layer Two-Process Daemon
3. Job Scheduler polling
4.native Layer Two-Process Guardian
Today we will only write about the first three ways, and the fourth native layer, two-process guardian, will be explained in the next article.
Let's first look at the priority of the process:
-
Front-end process
- Activity has invoked the onResume() method - Service service has invoked startForeground() - Service (onCreate(), onStart(), or onDestroy()) for life cycle callbacks - BroadcastReceiver executing its onReceive() method ,
-
Visible process
- Activity that is not in the foreground but is still visible to the user (such as calling its onPause() method) - Bind to Service for Visible (or Front Office) Activity
- Service process
- The startService() method starts services that do not fall into the above two categories -
Background process
- Activity's onStop() method has been invoked by a process that is not visible to the user
-
Empty process
- Process without any active application components
Let's see when the process will be killed:
1. Insufficient application memory, recycling process
Increase process priority and reduce process oom_adj value, such as setForeground() to boot process to increase process priority Release occupied resources when the application falls back to the background, because when oom_adj is the same, priority is given to releasing memory-intensive processes. The process that has been running in the background must be light.
2. System third party cleaning software, killing process
Using aidl to realize two-process daemon White List
3. The major rom vendors will clean up the killing process when the application exits
Using NDK, poll to see if the specified process is killed, and if the fork process is killed, start Using JobScheduler, poll to see if the specified process has been killed, and if so, start
Two-process daemon (based on java layer)
Here we will use aidl, some students who do not know can understand for themselves, let's first code:
1. Writing aidl interface
interface ProcessConnect { }
There is nothing in the interface. This is just to listen for disconnection. If disconnection occurs, the code starts the service.
2. Work Services
public class MessageService extends Service { private String TAG = "MessageService"; private int ID=0X00022; private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = this; new Thread(new Runnable() { @Override public void run() { while (true) { Log.e(TAG, "MessageService====>print"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //assert uri String path = "file:///android_asset/xiaoxin.wav"; Notification.Builder builder = new Notification.Builder(mContext); Notification notification = builder .setContentText("messageservice") .setSmallIcon(R.drawable.ting) .setSound(Uri.parse(path)) .build(); startForeground(ID,notification); bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY); return START_STICKY; } public ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG, "MessageService====>onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName name) { startService(new Intent(MessageService.this,GuardService.class)); bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY); } }; @Nullable @Override public IBinder onBind(Intent intent) { return new ProcessConnect.Stub() { }; } }
3. Guardian Service
public class GuardService extends Service { private Context mContext; private int ID=0X00021; @Override public void onCreate() { super.onCreate(); mContext = this; } @Override public int onStartCommand(Intent intent, int flags, int startId) { //assert uri String path = "file:///android_asset/xiaoxin.wav"; Notification.Builder builder = new Notification.Builder(mContext); Notification notification = builder .setContentText("GuardService") .setSmallIcon(R.drawable.ting) .setSound(Uri.parse(path)) .build(); startForeground(ID,notification); bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY); return START_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { return new ProcessConnect.Stub(){ }; } public ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e("GuardService", "GuardService====>onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName name) { startService(new Intent(GuardService.this,MessageService.class)); bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY); } }; }
As you can see from the above two services, whenever one service ends, another service starts it to realize that the process is not shut down.
4.MainActivity Opens Services
startService(new Intent(this,MessageService.class)); startService(new Intent(this,GuardService.class));
5. Configuration
<service android:name=".MessageService"></service> //Running in the new process <service android:name=".GuardService" android:process=":guardservice"></service>
The main five steps are done. It's very simple, but don't be too happy, because this two-process daemon method can only be effective below 4.0, but only partially useful for more than 4.0 models. Finally, let's look at using Job Scheduler to poll to start the killing process.
JobScheduler
In android development, there are scenarios where you need to perform a task at a later point or when a particular condition is met, such as when the device is connected to a power adapter or to WIFI. Fortunately, in API 21 (android 5.0, or Lollipop), google provides a new component called the Job Scheduler API to handle such scenarios.
When a set of preset conditions are met, the Job Scheduler API performs an operation for your application. Unlike Alarm Manager, this execution time is uncertain. In addition, the JobScheduler API allows multiple tasks to be performed simultaneously. This allows your application to perform certain specified tasks without considering the battery consumption caused by timing control.
Next we use Job Scheduler to start our killed service:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class JobWakeUpService extends JobService { private JobScheduler service; private int JobId=100; @Override public int onStartCommand(Intent intent, int flags, int startId) { JobInfo info = new JobInfo.Builder(JobId,new ComponentName(this,JobWakeUpService.class)) .setPeriodic(2000) .build(); service = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); service.schedule(info); return START_STICKY; } @Override public boolean onStartJob(JobParameters params) { Log.e("JobWakeUpService", "JobWakeUpService====>print"); //Start a Timing Task if(!isServiceWork(this,MessageService.class.getName())){ // startService(new Intent(this,MessageService.class)); } return false; } @Override public boolean onStopJob(JobParameters params) { //Stop it service.cancel(JobId); // service.cancelAll(); return false; } private boolean isServiceWork(Context context,String serviceName){ ActivityManager am= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100); if(runningServices == null){ return false; } for (ActivityManager.RunningServiceInfo service : runningServices) { String className = service.service.getClassName(); if(className.equals(serviceName)){ return true; } } return false; } }
We see here the onStartJob of our JobWakeUpService is called in a loop using the JobScheduler service.
Let's look at the configuration next:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission> <service android:name=".JobWakeUpService" android:enabled="true" android:permission="android.permission.BIND_JOB_SERVICE" ></service>
Call:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startService(new Intent(this, JobWakeUpService.class)); }
This will enable polling to see if the specified process has been killed, if killed, start the function.
Maybe you want to ask if this way can solve the problem that processes above 5.0 are not killed? I can only regret to tell you that, no, my test on Huawei 7.0 failed.
We have seen so many ways that we can't solve the problem that the process is not killed. Is there a better way?
In the next article, we will explain how to use NDK to implement two-process daemon.