I. Common survival schemes
**1. Monitor broadcast: ** monitors global static broadcast, such as time update broadcast, startup broadcast, unlock screen, network status, unlock and lock bright screen and dark screen (version 3.1). In the higher version, the application needs to run once after startup to monitor these system broadcasts. Can change the idea, do the APP after the start of the live (listen to the broadcast start live service)
** Timer and JobScheduler: ** If the application is killed by the system, the timer becomes invalid and the scheme becomes invalid. JobService is very important in 5.0,5.1,6.0, 7.0 (APP can be authorized in power management)
**3, dual process (NDK Fork child process), dual Service daemon: ** later version invalid, system reclaim policy changed to process group since 5.0. In the two-service scheme, the application is killed, and no background Service can run properly
** * improves the Service priority: ** only partially alleviates the immediate collection of services
Second, keep alive
-
1. AIDL single-process or dual-process Service
-
2. Lower the value of oOM_adj: permanently disable Notification by starting another service, does not affect the value of oOM_adj, overwrite the view of getWindow() with a “1-pixel” Activity, loop the silent audio (black tech, 7.0 can’t kill it)
-
3. Listen for screen-lock broadcasts: Keep the Activity in the foreground
-
4. Use custom lock screen interface: it covers the system lock screen interface.
-
Create a process for the Service using the Android: Process attribute
-
6. Jump to the system whitelist interface for users to add apps to the whitelist
Third, the resurrection
-
1. JobScheduler: the principle is similar to timer, 5.0,5.1,6.0 play a great role, 7.0 has some influence (APP can be authorized in power management)
-
2. Push each other to wake up and revive: Aurora, Umeng, and push of major manufacturers
-
3. Wake up each other with APP broadcast of the same faction: such as Toutiao and Ali
Program implementation effect statistics
1. Two-process daemon solution (based on onStartCommand() return START_STICKY)
-
1. Native 5.0, 5.1: Native taskbar swipe to clean up the app, the Service is killed, pulled up, and kept alive
-
2. Gionee F100(5.1) : Kill the entire APP directly, including the double daemons, with one click of clearing. After testing, the screen can be locked for at least 40 minutes without manual cleaning
-
3. Huawei Enjoy 5X (6.0) : Kill the entire APP directly, including the double daemons, with one click of clearing. If you do not manually clean the screen, the lock screen only lasts for 10 seconds. Conclusion: The two-process daemon scheme is invalid.
-
4. Meitu M8S (7.1.1) : One-click cleanup directly kills the entire APP, including the double daemons. If not cleaned, the lock screen will be killed (9 minutes or so), then resurrected, then repeatedly killed and resurrected. Conclusion: Dual daemons can continuously pull services in the background.
-
5. Native 7.0: Service lives after APP is cleared from taskbar. The Service survives using this scenario.
-
6. LG V30+(7.1.2) : Without dual-process daemons, one-click cleanup cannot kill services. After adding this solution, the service cannot be killed and the lock screen can survive (test observation > 50 minutes).
-
7. Mi 8(8.1) : One-click cleanup directly kills app and includes dual daemons. In the case of no cleanup, the solution without daemon and the solution with daemon Service remain open for about 12 minutes. Conclusion: This program did not work
** Conclusion: ** Except huawei this scheme is invalid and does not work for vendors that have not changed the underlying (the START_STICKY field can keep the Service from being killed). This scheme can be mixed with other schemes
Start 1 pixel Activity (based on onStartCommand() return START_STICKY)
-
1, native 5.0, 5.1:3s service is disabled after screen lock and restart (START_STICKY field is enabled)
-
2. Huawei 5X (6.0) : The lock screen only lasts for 4s. Conclusion: The scheme is invalid.
-
3. Meitu M8S (7.1.1) : same as native 5.0
-
Native 7.0: M8S.
-
5. LG V30+(7.1.2) : The situation after the screen lock is the same as that after the screen lock is not added, and the service keeps running. Conclusion: This scheme does not work
-
6. Mi 8(8.1) : All apps are eliminated after 2s. Conclusion: This program did not work
** Conclusion: ** This program is ineffective
3, deliberately playing silent music in the background (based on onStartCommand() return START_STICKY)
-
1, native 5.0, 5.1:3s service is disabled after screen lock and restart (START_STICKY field is enabled)
-
2. Huawei Enjoy 5X (6.0) : The service still exists after one-click clearing. The service can only be killed after a separate clearing. Conclusion: This scheme is suitable
-
3. Meitu M8S (7.1.1) : same as 5.0
-
4. Native 7.0: the service will be destroyed after APP closure in task manager, and will be resurrected after about 3s (same as START_STICKY field mode only). Conclusion: It is not clear whether this scheme works
-
5. LG V30+(7.1.2) : The effect is consistent before and after using this scheme. Conclusion: This program does not work
-
6. Xiaomi 8(8.1) : One-click cleaning can kill services. Keep the screen alive for more than 20 minutes
** Conclusion: ** successfully keeps Huawei mobile phone alive. The Mi 8 also managed to break 20 minutes
4. Wake up Service using JobScheduler (based on onStartCommand() return START_STICKY)
-
1. Native 5.0, 5.1: After the APP is killed in task Manager, the service will restart after the cycle time. Conclusion: This scheme works
-
2. Huawei Enjoy 5X (6.0) : The APP is killed by one-click clearing. After about 12 seconds, the service will automatically restart and JobScheduler works
-
3. Meitu M8S (7.1.1) : Kill the APP directly by one-click cleaning, and it cannot be restarted automatically
-
Native 7.0: M8S (7.1.1)
-
5. Xiaomi 8(8.1) : Tongmeitu M8S (7.1.1)
** Conclusion: ** only has effect on 5.0, 5.1 and 6.0
5, mixed effect, and pop-up notification in the notification bar
-
1. Native 5.0, 5.1: After the APP is killed in task Manager, the service will restart after the cycle time. Keep the screen locked for more than 11 minutes
-
2. Huawei Enjoy 5X (6.0) : After one-click cleaning, the service still exists. You can kill the service only after cleaning it separately. Conclusion: The scheme is suitable.
-
3. Meitu M8S (7.1.1) : One-click clearing APP will be killed. In normal cases, services still exist after the screen is locked.
-
4. Native 7.0: After closing APP in task Manager, the service will be killed and will be resurrected after 2 seconds
-
5. Mi 8(8.1) : One-click clearing can kill the service, and the background can stay alive for more than 38 minutes under the lock screen
-
6. Honor 10 (8.0) : One-click clearing and killing service, keeping the background alive for more than 23 minutes under the lock screen
** Conclusion: pop-up notification bar, dual process and silent music can be used to improve the survival probability of background service in ** high version
Implementation process
A dual process implementation scheme
Use the AIDL binding mode to create two new Service priorities (to prevent the Service from being killed by the system at the same time). Daemons with different priorities pull each other, and determine in the binding callback of each daemon’s ServiceConnection whether the Service needs to be pulled up again and rebound to the daemon thread.
1. Create an AIDL file
KeepAliveConnection
interface KeepAliveConnection {
}
Copy the code
2, create a new service class StepService, onBind() method returns new Keepaliveconnection.stub () object, and in ServiceConnection binding callback to start and bind the daemon service class GuardService.
/** * Communication between two main processes ** @author LiGuangMin
* @time Created by 2018/8/17 11:26
*/
public class StepService extends Service {
private final static String TAG = StepService.class.getSimpleName();
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Logger.d(TAG, "StepService: Create a link");
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if(! isServiceRunning) { Intent i = new Intent(StepService.this, DownloadService.class); startService(i); Override public void onServiceDisconnected(ComponentName ComponentName) {// Disconnect startService(new) Intent(StepService.this, GuardService.class)); // rebindbindService(new Intent(StepService.this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT); }}; @Nullable @Override public IBinder onBind(Intent intent) {return new KeepAliveConnection.Stub() {}; } @Override public int onStartCommand(Intent intent, int flags, int startId) { startForeground(1, new Notification()); // bind establishes the linkbindService(new Intent(this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT);
returnSTART_STICKY; }}Copy the code
3. Do the same for the GuardService daemon as in 2
/** * communication between two daemon processes ** @author LiGuangMin
* @time Created by 2018/8/17 11:27
*/
public class GuardService extends Service {
private final static String TAG = GuardService.class.getSimpleName();
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Logger.d(TAG, "GuardService: Build links");
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if(! isServiceRunning) { Intent i = new Intent(GuardService.this, DownloadService.class); startService(i); Override public void onServiceDisconnected(ComponentName ComponentName) {// Disconnect startService(new) Intent(GuardService.this, StepService.class)); // rebindbindService(new Intent(GuardService.this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT); }}; @Nullable @Override public IBinder onBind(Intent intent) {return new KeepAliveConnection.Stub() {}; } @Override public int onStartCommand(Intent intent, int flags, int startId) { startForeground(1, new Notification()); // bind establishes the linkbindService(new Intent(this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT);
returnSTART_STICKY; }}Copy the code
4. In the Activity, start the DownloadService that needs to be preserved and then start the two-process that needs to be preserved
public class MainActivity extends AppCompatActivity {
private TextView mShowTimeTv;
private DownloadService.DownloadBinder mDownloadBinder;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mDownloadBinder = (DownloadService.DownloadBinder) service;
mDownloadBinder.setOnTimeChangeListener(new DownloadService.OnTimeChangeListener() {
@Override
public void showTime(final String time) {
runOnUiThread(new Runnable() {
@Override
public void run() { mShowTimeTv.setText(time); }}); }}); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
Intent intent = new Intent(this, DownloadService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); // Double daemons with different priorities startAllServices(); } @Override public voidonContentChanged() {
super.onContentChanged();
mShowTimeTv = findViewById(R.id.tv_show_time);
}
@Override
protected void onDestroy() { super.onDestroy(); unbindService(mServiceConnection); } /** * enable all daemon Service */ private voidstartAllServices() { startService(new Intent(this, StepService.class)); startService(new Intent(this, GuardService.class)); }}Copy the code
2. After listening to the lock screen broadcast, use “1” pixel Activity to improve the priority
1. The Activity’s View is set to 1 pixel and set to the Window object. Wake up the service by making a survival judgment on the Activity’s onDestroy cycle. The 1 pixel “Activity is shown below
public class SinglePixelActivity extends AppCompatActivity {
private static final String TAG = SinglePixelActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window mWindow = getWindow();
mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams attrParams = mWindow.getAttributes();
attrParams.x = 0;
attrParams.y = 0;
attrParams.height = 1;
attrParams.width = 1;
mWindow.setAttributes(attrParams);
ScreenManager.getInstance(this).setSingleActivity(this);
}
@Override
protected void onDestroy() {
if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) {
Intent intentAlive = new Intent(this, DownloadService.class);
startService(intentAlive);
}
super.onDestroy();
}
}
Copy the code
2. Monitor the broadcast, encapsulate it into a ScreenReceiverUtil class, and monitor the dynamic registration of the broadcast unlocked by the screen lock
public class ScreenReceiverUtil {
private Context mContext;
private SreenBroadcastReceiver mScreenReceiver;
private SreenStateListener mStateReceiverListener;
public ScreenReceiverUtil(Context mContext) {
this.mContext = mContext;
}
public void setScreenReceiverListener(SreenStateListener mStateReceiverListener) { this.mStateReceiverListener = mStateReceiverListener; This.mscreenreceiver = new SreenBroadcastReceiver(); 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 voidstopScreenReceiverListener() { mContext.unregisterReceiver(mScreenReceiver); } /** * Public interface SreenStateListener {void onSreenOn(); void onSreenOff(); void onUserPresent(); } public class SreenBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction();if (mStateReceiverListener == null) {
return;
}
if(Intent. ACTION_SCREEN_ON. Equals (action)) {/ / tail mStateReceiverListener onSreenOn (); }else if(Intent. ACTION_SCREEN_OFF. Equals (action)) {/ / lock screen mStateReceiverListener. OnSreenOff (); }else if(Intent. ACTION_USER_PRESENT. Equals (action)) {/ / unlock mStateReceiverListener onUserPresent (); }}}}Copy the code
Create a ScreenManager class for the 1-pixel Activity to prevent memory leaks
public class ScreenManager {
private static final String TAG = ScreenManager.class.getSimpleName();
private static ScreenManager sInstance;
private Context mContext;
private WeakReference<Activity> mActivity;
private ScreenManager(Context mContext) {
this.mContext = mContext;
}
public static ScreenManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new ScreenManager(context);
}
returnsInstance; } /** Get a reference to SinglePixelActivity * @param Activity */ public voidsetSingleActivity(Activity activity) { mActivity = new WeakReference<>(activity); } /** * Start SinglePixelActivity */ public voidstartActivity() { Intent intent = new Intent(mContext, SinglePixelActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } /** * end SinglePixelActivity */ public voidfinishActivity() {
if(mActivity ! = null) { Activity activity = mActivity.get();if(activity ! = null) { activity.finish(); }}}}Copy the code
4. Create a New SingleActivityStyle file for the 1 pixel Style
<style name="SingleActivityStyle" parent="android:Theme.Holo.Light.NoActionBar">
<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:windowContentOverlay">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowNoDisplay">false</item>
Copy the code
5, Let SinglePixelActivity use singleInstance launch mode in the manifest file
<activity
android:name=".activity.SinglePixelActivity"
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:theme="@style/SingleActivityStyle" />
Copy the code
6. Register the monitored broadcast and control the SinglePixelActivity in the live service DownloadService.
public class DownloadService extends Service {
public static final int NOTICE_ID = 100;
private static final String TAG = DownloadService.class.getSimpleName();
private DownloadBinder mDownloadBinder;
private NotificationCompat.Builder mBuilderProgress;
private NotificationManager mNotificationManager;
private ScreenReceiverUtil mScreenListener;
private ScreenManager mScreenManager;
private Timer mRunTimer;
private int mTimeSec;
private int mTimeMin;
private int mTimeHour;
private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
@Override
public void onSreenOn() {
mScreenManager.finishActivity();
Logger.d(TAG, "1 pixel Activity turned off");
}
@Override
public void onSreenOff() {
mScreenManager.startActivity();
Logger.d(TAG, "Open 1 pixel Activity");
}
@Override
public void onUserPresent() {}}; private OnTimeChangeListener mOnTimeChangeListener; @Override public voidonCreate() { super.onCreate(); MScreenListener = new ScreenReceiverUtil(this); mScreenManager = ScreenManager.getInstance(this); mScreenListener.setScreenReceiverListener(mScreenListenerer); mDownloadBinder = new DownloadBinder(); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Logger.d(TAG,"onStartCommand");
startRunTimer();
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mDownloadBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Logger.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (mManager == null) {
return;
}
mManager.cancel(NOTICE_ID);
stopRunTimer();
// mScreenListener.stopScreenReceiverListener();
}
private void startRunTimer() {
TimerTask mTask = new TimerTask() {
@Override
public void run() {
mTimeSec++;
if (mTimeSec == 60) {
mTimeSec = 0;
mTimeMin+ +; }if (mTimeMin == 60) {
mTimeMin = 0;
mTimeHour++;
}
if (mTimeHour == 24) {
mTimeSec = 0;
mTimeMin = 0;
mTimeHour = 0;
}
String time = "Time is:" + mTimeHour + ":" + mTimeMin + ":" + mTimeSec;
if(mOnTimeChangeListener ! = null) { mOnTimeChangeListener.showTime(time); } Logger.d(TAG, time); }}; mRunTimer = new Timer(); // Update mruntimer.schedule (mTask, 1000, 1000) every 1s; } private voidstopRunTimer() {
if(mRunTimer ! = null) { mRunTimer.cancel(); mRunTimer = null; } mTimeSec = 0; mTimeMin = 0;
mTimeHour = 0;
Logger.d(TAG, "Time is:" + mTimeHour + ":" + mTimeMin + ":" + mTimeSec);
}
public interface OnTimeChangeListener {
void showTime(String time);
}
public class DownloadBinder extends Binder {
public void setOnTimeChangeListener(OnTimeChangeListener onTimeChangeListener) { mOnTimeChangeListener = onTimeChangeListener; }}}Copy the code
3. Play music in the background
1. Create a new Service class for playing music and change the playback mode to an infinite loop. Restart yourself in its onDestroy method.
public class PlayerMusicService extends Service {
private final static String TAG = PlayerMusicService.class.getSimpleName();
private MediaPlayer mMediaPlayer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Logger.d(TAG, TAG + "---->onCreate, start the service");
mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
mMediaPlayer.setLooping(true);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
startPlayMusic();
}
}).start();
return START_STICKY;
}
private void startPlayMusic() {
if(mMediaPlayer ! = null) { Logger.d(TAG,"Start background music");
mMediaPlayer.start();
}
}
private void stopPlayMusic() {
if(mMediaPlayer ! = null) { Logger.d(TAG,"Turn off background music");
mMediaPlayer.stop();
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopPlayMusic();
Logger.d(TAG, TAG + "---->onCreate, stop service"); Intent Intent = new Intent(getApplicationContext(), playerMusicService.class); startService(intent); }}Copy the code
2. Start PlayerMusicService in the onCreate method of the live DownloadServie service class
Intent intent = new Intent(this, PlayerMusicService.class);
startService(intent);
Copy the code
3. Register in the Manifest file
<service
android:name=".service.PlayerMusicService"
android:enabled="true"
android:exported="true"
android:process=":music_service" />
Copy the code
4. Wake up the Service using JobScheduler
1. Create a ScheduleService class that inherits from JobService and restart the DownloadService based on its onStartJob callback.
public class ScheduleService extends JobService {
private static final String TAG = ScheduleService.class.getSimpleName();
@Override
public boolean onStartJob(JobParameters params) {
boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
if(! isServiceRunning) { Intent i = new Intent(this, DownloadService.class); startService(i); Logger.d(TAG,"ScheduleService starts DownloadService");
}
jobFinished(params, false);
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false; }}Copy the code
2. Register and use JobScheduler in the DownloadService service class
/** * Use JobScheduler to hold work */ private voiduseJobServiceForKeepAlive() {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (jobScheduler == null) {
return; } jobScheduler.cancelAll(); JobInfo.Builder builder = new JobInfo.Builder(1024, new ComponentName(getPackageName(), ScheduleService.class.getName())); // Set the cycle to 2s Builder.setperiodic (1000 * 2); builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
int schedule = jobScheduler.schedule(builder.build());
if (schedule <= 0) {
Logger.w(TAG, "The schedule error!"); }}Copy the code
3. Set permissions in the manifest file
<service
android:name=".service.ScheduleService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
Copy the code
About push class pull
Integrate HUAWEI Push according to HUAWEI official documents
-
1. Huawei Fun 5X(6.0) : The APP can be pulled up when all processes are killed.
-
2. Huawei NoVE 3E (8.0) : The APP cannot be pulled up when all processes are killed and can receive push.
-
3. Huawei Honor 10 (8.1) : same as 2
** Conclusion: Under the theory of **, Huawei push should be able to pull up Huawei machines, I feel that is the reason WHY I did not spend money
Add: The ServiceAliveUtils class is as follows
public class ServiceAliveUtils {
public static boolean isServiceAlice() {
boolean isServiceRunning = false;
ActivityManager manager =
(ActivityManager) MyApplication.getMyApplication().getSystemService(Context.ACTIVITY_SERVICE);
if (manager == null) {
return true;
}
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("demo.lgm.com.keepalivedemo.service.DownloadService".equals(service.service.getClassName())) {
isServiceRunning = true; }}returnisServiceRunning; }}Copy the code
Author: minminaya links: www.jianshu.com/p/b5371df6d…
To read more
Do you choose Java, Go, or PHP for background?
NDK project actual combat – high imitation 360 mobile phone assistant uninstall monitoring
AndroidUtils: The Utils Android developers have to keep in their collections
(Android) Interview Questions
Google Developer Conference: Tensorflow tips you need to know
Believe in yourself, there is nothing impossible, only unexpected
It’s not just technology that’s gained here!