I mean, I don’t think it’s cool to force a live app, but I have to, in order to fulfill my needs.

At the beginning, the scheme I tried was JobScheduler provided by Android 5.0 system, which can set conditions in advance and automatically start JobService when the conditions are reached. It can be used happily under Android 8.0. However, on Huawei Android 8.0, JobService cannot be invoked by the system after an application is killed.

Therefore, the dual process service binding method is adopted to realize the application survival function.

Look directly at the schematic diagram:

The principle is to use the Binder obituary mechanism. If a Service Binder entity is killed, the system will send an obituary to the Client. So two Binder services can be started by two processes, as C/S for each other. If one process dies, the other process will receive a Binder obituary and the other process can be pulled.

What the transferActivities in the two processes are for will be discussed later.

I’ve written two applications here, one is AIDLServer, which is the server side; One is AIDLClient, which is the equivalent of a client. AIDL is used to communicate between the two processes.

// IMyAidlInterface.aidl
package com.wuzy.aidlserver;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

     void bindSuccess();

     void unbind();

}
Copy the code

Note that the AIDL files for both applications must be the same, including the package name.

Then, write two binder entity services RemoteService, LocalService, the main code is as follows:

public class RemoteService extends Service { private static final String TAG = "RemoteService"; @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate: create RemoteService"); bindLocalService(); } @Override public IBinder onBind(Intent intent) { return stub; } private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() { @Override public void bindSuccess() throws RemoteException {log. e(TAG, "bindSuccess: RemoteService succeeded "); } @override public void unbind() throws RemoteException {log. e(TAG, "unbind: remove RemoteService from LocalService "); getApplicationContext().unbindService(connection); }}; / private void bindLocalService() {Intent Intent = new Intent(); intent.setComponent(new ComponentName("com.wuzy.aidlclient", "com.wuzy.aidlclient.LocalService")); if (! getApplicationContext().bindService(intent, connection, Context.BIND_AUTO_CREATE)) { Log.e(TAG, "bindLocalService: Failed to bind LocalService "); stopSelf(); } } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name) { // bindRemoteService(); createTransferActivity(); }}; private void createTransferActivity() { Intent intent = new Intent(this, TransferActivity.class); intent.setAction(TransferActivity.ACTION_FROM_SELF); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); }}Copy the code
public class LocalService extends Service { private static final String TAG = "LocalService"; @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate: create LocalService"); bindRemoteService(); } @override public IBinder onBind(Intent Intent) {log. e(TAG, "onBind: LocalService"); return stub; } private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() { @Override public void bindSuccess() throws RemoteException {log. e(TAG, "bindSuccess: RemoteService binding LocalService successful "); } @Override public void unbind() throws RemoteException { getApplicationContext().unbindService(connection); }}; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name) { // bindRemoteService(); createTransferActivity(); }}; private void createTransferActivity() { Intent intent = new Intent(this, TransferActivity.class); intent.setAction(TransferActivity.ACTION_FROM_SELF); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } private void bindRemoteService() { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.wuzy.aidlserver", "com.wuzy.aidlserver.RemoteService")); if (! getApplicationContext().bindService(intent, connection, Context.BIND_AUTO_CREATE)) { Log.e(TAG, "bindRemoteService: Failed to bind RemoteService "); stopSelf(); }}}Copy the code

Bind to each other at onCreate, and restart the service bind to each other when onServiceDisconnected receives the obituary.

RemoteService cannot start LocalService when the LocalService process is killed, and vice versa.

So, here can only take the “curve to save the country” way. Through the TransferActivity transfer, it is ok to start the TransferActivity of the daemon first and then start the TransferActivity of the keepalive process from the TransferActivity of the daemon. Start LocalService from the TransferActivity of the keepalive process and bind the LocalService again, and vice versa. Of course, the TransferActivity should not be perceived by the user, otherwise it will be very obtrusive. Therefore, the TransferActivity here is always 1 pixel, which can be destroyed in time after completing the task.

The TransferActivity code will not be posted, you can go to GitHub for details.

This is generally fine because Binder obituaries come with the Binder framework, unless you kill two processes at once.

Finally, the purpose of the save is to do a task, so the save should be disabled when the task is complete, otherwise it is not nice to always hog memory.