Existing problems
Recently, I was working on A project A, which is already A system app, but the App is out of memory and the process will die after the user cleans up the background. In order to ensure that A is always running, A daemon is developed to keep and pull up A.
Requirements:
- After startup, A cannot start itself to ensure rapid startup and operation of the equipment
- After the user clears the background, A can continue to survive in order to answer real-time calls
- After the application is closed, A can be kept alive
- Memory usage is too large. After the system releases memory, A and service can survive
- After restart, the service can start automatically
Theoretical knowledge preparation
service explanation
Determine whether the APP is in the foreground or background
Process priority
The Android system keeps the application process as long as possible. In order to create a new process or run a more important process, the system will remove the old process and reclaim memory. According to the priority in the important hierarchy, the system gives priority to the processes with low importance.
Foreground process
The number of foreground processes is small. The system will terminate them only when there is insufficient memory.
- The onResume() method of the Activity has been called for the Activity that the managed user is interacting with
- Hosting a Service, which is bound to the Activity that the user is interacting with
- Hosting the Service service running in the foreground, startforegroup() has been called
- Hosting a Service (onCreate(), onStart(), or onDestroy()) that is executing a lifecycle callback
- Manages the BroadcastReceiver whose onReceive() method is executing
Visible process
The system will not terminate the visible process unless it must be terminated in order to ensure the operation of the foreground process.
- An Activity that hosts an Activity that is not in the foreground but is still visible to the user has called its onPause() method.
- Hosting services bound to visible (or foreground) activities.
Service process
- The service started by the startService() method is running and does not belong to the above two higher category processes.
The system tries to maintain the operation of service processes unless the system memory is insufficient to maintain the operation of foreground processes and visible processes.
Background process
- The process holds an activity invisible to the user. onStop() is not called. onDestory() is not called. The process is a background process.
The background process does not affect the user experience. The system can kill the background process arbitrarily for the foreground process, visible process and service process.
Empty process
- A process that does not contain any active application components
The purpose of keeping such processes for caching is to reduce the startup time required for the next time the component runs in them.
Common survival schemes
- Open the Activity of one pixel
- Front desk service
White keep alive: start the foreground service to raise the priority of the process to the foreground process, but the foreground service is bound to the notification, which will be displayed in the notification bar, and the user may delete the notification.
Gray keep alive: use the system vulnerability to open the front desk service and remove the notice, which can not be sensed by the user. - Mutual Awakening
Black keep alive: pull up each other, Tencent series. - JobSheduler
- Sticky service
- Dual process
Let the two processes protect each other. After one service is cleaned, the process that is not cleaned will restart the process, or even multiple processes, - Set service to START_STICKY will restart after being killed for 5 seconds, and startforeroundservice will set the process as the foreground process.
- Join the white list
Solution
Obtain system level permissions
Since project A is already A system app, add android:sharedUserId = "android.uid.system" in AndroidManifest
By setting the same User id, multiple applications can run in the same process. Set sharedUserId to Android uid. System can run the application and system application in the same process, so it has system permission.
Determine whether APP is alive
- Get all the app information of the system, and first traverse all the apps
- Judge whether the process is alive through the process id. if it is not alive, send a broadcast. If it is alive, do not process it. In the BroadcastReceiver, judge the action and start the activity.
//Judge whether the process is running according to the process name and return pid public static int getPidByProcessName(Context context, String procName) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> processInfoList = manager.getRunningAppProcesses(); if(processInfoList!=null){ for (ActivityManager.RunningAppProcessInfo processInfo : processInfoList) { LogUtils.d("Utils processName = " + processInfo.processName); if (procName.equals(processInfo.processName)) { LogUtils.d("Uitls pid = " + processInfo.pid); return processInfo.pid; } } } return -1; }
Remove activity interface
app without interface (for non system applications, service cannot be started without activity), but the theme @ Android: style / theme. Can be used Hide the activity with nodisplay to achieve the effect of app without interface.
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.NoDisplay">
Note:
- Myactivity extensions can only be activities, not other topics. For example, AppCompatActivity (support_v4/v7, with actionbar or toolbar) does not match activities, resulting in app running errors.
- Since it is a hidden app, desktop icons and background tasks cannot be seen by users. After testing and verification, if the desktop icon and background task are not hidden, there will be no response after clicking, like a crash.
<activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" android:launchMode="singleInstance" android:excludeFromRecents="true" //The background task icon is set to true > <intent-filter> <action android:name="android.intent.action.MAIN" /> <!--<category android:name="android.intent.category.LAUNCHER" />--> //Desktop startup icon removal </intent-filter> </activity> <service android:name=".service.TestService" android:enabled="true" android:exported="true"></service>
Override the onResume() method in the activity to kill the activity.
@Override protected void onResume() { super.onResume(); this.finish(); }
Daemon
Static configuration receiver
Daemon startup
<receiver android:name=".service.TestReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
Implementation of broadcast receiver class
Accept in the Receiver and judge the action
public class TestReceiver extends BroadcastReceiver { public final static String TEST_ACTION_BOOT_COMPLETED="android.intent.action.BOOT_COMPLETED"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(TEST_ACTION_BOOT_COMPLETED.equals(action)){ Intent intentStart = new Intent(context, MainActivity.class); intentStart.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intentStart); } } }
Service
The getPidByProcessName method is used to regularly judge whether the process is alive. If it is alive, it will not be processed; If not, send A broadcast to process A.
Configure action in process A and pull up the corresponding Activity after receiving the broadcast.
Due to the limited level of the author, if there are any inaccuracies in the article, you are welcome to point out. If you have any questions, please discuss them below.