This article is the last in the series of background kill, which mainly discusses the survival of the process. Android itself is very kind when it is designed, it expects the APP to know how to release the process when it is not visible or in some other scenarios, but Android underestimates the “greed”, especially many domestic apps. They only hope to improve their performance by asking for more, regardless of other apps or systems, resulting in a serious waste of resources, which is also the biggest reason why Android is criticized by iOS. In this paper, there are two ways to keep alive: the process of law-abiding and the process of rogue means to keep alive.
Statement: Firmly oppose the rogue means to achieve process preservation firmly oppose the rogue process preservation firmly oppose the rogue process preservation firmly “please tell the product: cannot enter the white list”
- Normal law-abiding process preservation: memory clipping (good student APP to use)
- Keep rogue progress alive, improve priority (good student APP don’t use)
- Binder obituaries (good student APP not to use)
A process save for LowmemoryKiller
LowmemoryKiller scans all user processes when memory is low and kills those that are not important. Depending on current memory usage, LowmemoryKiller kills more processes when memory is low. In the kernel, lowmemorykiller.c defines several levels of memory reclamation as follows :(this may vary from version to version)
static short lowmem_adj[6] = {
0.1.6.12};static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
3 * 512./* 6MB */
2 * 1024./* 8MB */
4 * 1024./* 16MB */
16 * 1024./* 64MB */
};
static int lowmem_minfree_size = 4;Copy the code
Each value in lowmem_adj represents the warning level of the threshold, and lowmem_minfree represents the remaining memory of the corresponding level. The two correspond one by one. For example, when the available memory of the system is less than 6MB, the warning level is 0. When the available memory of the system is less than 8M but greater than 6M, the warning level is 1. When the available memory is less than 64MB and greater than 16MB, the alert level is 12. LowmemoryKiller obtains the current alert level based on the available memory of the current system. If the oOM_ADJ of a process is greater than the alert level and occupies the largest memory, the process is killed first. The smaller the omm_ADJ, the more important the process. Some foreground processes, oOM_adj, are small and some background services, omm_adj, are large, so when running out of memory, Lowmemorykiller kills the background services first, not the foreground processes. For LowmemoryKiller, it is important to say that processes with the same OMM_ADJ consume more memory, so if our APP goes into the background, we should try to release unnecessary resources to reduce the risk of being killed. So how to release? OnTrimeMemory is a good opportunity, and onLowmemory may be the last straw, below review, LowmemoryKiller how to kill processes, a simple look at the source code (4.3) :(other versions of the principle is similar)
staticint lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) { ... <! Key points -1Int other_free = global_page_state(NR_FREE_PAGES); int other_file = global_page_state(NR_FILE_PAGES);<! -- Key point 2 find min_adj -->
for(i = 0; i < array_size; i{+ +)if (other_free < lowmem_minfree[i] &&
other_file < lowmem_minfree[i]) {
min_adj = lowmem_adj[i];
break; }} <!--The key point3findp->Oomkilladj > min_adj and oomkilladj is the largest, most memory process - > for_each_process (p) {/ / find the first one is greater than or equal to the min_adj, priority is lower than the threshold of the if (p - > oomkilladj< min_adj || !p->mm) continue; Tasksize = get_mm_rss(p->mm); if (tasksize<= 0)
continue;
if (selected) {// find the one with the lowest priority and a large memory footprintif (p->oomkilladj < selected->oomkilladj)
continue;
if (p->oomkilladj == selected->oomkilladj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
selected_tasksize = tasksize;
lowmem_print(2,"select %d (%s), adj %d.size %d.to kill\n",
p->pid, p->comm, p->oomkilladj, tasksize); } if(selected ! = NULL) {... force_sig(SIGKILL, selected); } return rem; }Copy the code
LowmemoryKiller will run through all processes to find low-priority and high-memory processes. If p->oomkilladj>min_adj indicates that the process can be killed, LowmemoryKiller will send a SIGKILL signal to kill the process. Note that LMKD first looks for processes with the same priority. The process with the highest memory footprint is killed first, which gives us two ways to package the process:
- 1. Increasing the priority of a process means reducing the p->oomkilladj of a process.
- 2. Reduce the memory usage of APP. When the OOM_ADJ is the same, the process with large memory consumption will be preferentially eliminated
However, in most cases, Android’s process priority management is reasonable. Even if some scenarios require special measures to improve the priority, Android also provides a reference scheme, such as audio playback, UI hiding, need to set the Sevice process to a specific priority to prevent being killed by the background. For example, some backup processes also need some special processing, but these are within the scope of Android, so in most cases, Android does not recommend the APP to increase the priority, because it would be against Android’s own process management, in other words, rogue. The second case is discussed here, which reduces the risk of being killed by releasing the memory reasonably. The landlord does not want to be killed, so he can only pay public grain and cut himself off. However, it also depends on the timing of the cut, when it is more cost-effective to lose weight. There is an onTrimeMemory function involved here, which is a system callback function. The main function is that the Android system gives the APP a memory cutting grade after comprehensive evaluation. For example, when the memory is sufficient and the APP returns to the background, You will receive TRIM_MEMORY_UI_HIDDEN level pruning, which tells APP to release some UI resources, such as a large amount of image memory. Some scenes that introduce image browsing cache may need to release UI resources even more. Here is the callback time of onTrimeMemory and the corresponding processing should be done by APP.
OnTrimeMemory’s callback timing and memory pruning level
OnTrimMemory is a callback interface introduced in Android 4.0. Its main function is to inform applications to slim themselves in different scenarios, release memory, reduce the risk of being killed by the background, and improve user experience. As the adaptation of APP is basically above 14 at present, there is no need to consider compatibility issues. You can override the OnTrimMemory function in your Application or Activity to respond to the system’s call:
public class LabApplication extends Application {
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
// Trim the memory based on the level}}Copy the code
OnTrimeMemory supports different clipping levels. For example, when APP enters the background through HOME construction, its priority (OOM_ADJ) changes and the onTrimeMemory callback is never triggered. At this time, the clipping level given by the system is generally TRIM_MEMORY_UI_HIDDEN. This means that the UI is hidden, and uI-related, memory intensive resources, such as a large image cache, can be released, as well as many other scenarios with different clipping levels. Therefore, two questions need to be clarified:
- 1. How are different clipping levels generated and what are their meanings
- 2. How APP releases memory resources according to different clipping levels (self-clipping degree)
First look at ComponentCallbacks2 definition of the meaning of different cutting levels: here defined a total of 4+3 a total of 7 cutting levels, why is it 4+3? Because there are 4 for background processes and 3 for foreground (RUNNING) processes, the target objects are different, see the specific analysis
Cut class | The numerical | The target process |
---|---|---|
TRIM_MEMORY_COMPLETE | 80 | Background processes |
TRIM_MEMORY_MODERATE | 60 | Background processes |
TRIM_MEMORY_BACKGROUND | 40 | Background processes |
TRIM_MEMORY_UI_HIDDEN | 20 | Background processes |
TRIM_MEMORY_RUNNING_CRITICAL | 15 | Foreground RUNNING process |
TRIM_MEMORY_RUNNING_LOW | 10 | Foreground RUNNING process |
TRIM_MEMORY_RUNNING_MODERATE | 5 | Foreground RUNNING process |
Its meaning is as follows:
-
TRIM_MEMORY_UI_HIDDEN All UI interfaces of the current application are not visible. Usually, the UI interface of the application is not visible after the user clicks the Home or Back key. In this case, some UI-related resources should be released. TRIM_MEMORY_UI_HIDDEN is the most frequently used clipping level. Official documents: the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed
-
TRIM_MEMORY_BACKGROUND The current phone is currently short of memory (the number of background processes is low), and the system starts to clean processes according to the LRU cache. This program is located at the head of the LRU cache list and is unlikely to be cleaned, but freeing up some resources that are easy to recover can improve the phone’s operating efficiency. It also ensures recovery speed. Official documents: the process has gone on to the LRU list. This is a good opportunity to clean up resources that can efficiently and quickly be re-built if the user returns to the app
-
TRIM_MEMORY_MODERATE Current Mobile phone memory is tight (the number of background processes is low). The system starts to clean processes according to the LRU cache. This program is located in the middle of the LRU cache list and should release more memory to improve operating efficiency. The process is around the middle of the background LRU list; freeing memory can help the system keep other processes running later in the list for better overall performance.
-
TRIM_MEMORY_COMPLETE The current phone is running out of memory (the number of background processes is low), and the system starts to clean processes based on the LRU cache. This program is located at the very edge of the LRU cache list. The system kills this process first and should free up all available memory. The process is traversing the end of the background LRU list, and if more memory isn’t found soon it will be killed.
The following three levels are for foreground running applications
-
TRIM_MEMORY_RUNNING_MODERATE Indicates that the process is foreground or visible and is running properly and is not normally killed, but currently the phone is a bit tight (background and empty processes are running low) and the system has started clearing memory, freeing some if necessary. Official documents: the process is not an expendable background process, but the device is running moderately low on memory. Your running process may want to release some unneeded resources for Use elsewhere.
-
TRIM_MEMORY_RUNNING_LOW indicates that the process is foreground or visible, runs normally and is not killed. However, the phone is currently under pressure (background and empty processes are killed in large numbers) and should free up unnecessary resources to improve system performance. Official documents: the process is not an expendable background process, but the device is running low on memory. Your running process should free up unneeded resources to allow that memory to be used elsewhere.
-
TRIM_MEMORY_RUNNING_CRITICAL indicates that the process is a foreground or visible process, but the phone is very memory constrained (background and empty processes are almost all killed). In this case, you should free up any unnecessary resources as much as possible. Otherwise, the system may kill all cached processes. And kill processes that should be kept running. Official documents: the process is not an expendable background process, but the device is running extremely low on memory and is about to not be able to keep any background processes running. Your running process should free up as many non-critical resources as it can to allow that memory to be used elsewhere. The next thing that will happen after this is called to report that nothing at all can be kept in the background, a situation that can start to notably impact the user.
The above abstract description of the meaning of the established parameters of Android, the following look at the timing and principle of onTrimeMemory callback, here using 6.0 code analysis, because 6.0 is much clearer than the previous 4.3 code: When the user’s actions cause the APP priorities to change, updateOomAdjLocked is called to update the priorities of the process. During the update, the LRU process list is scanned and the oOM_adj of the process is recalculated. In addition, the current system condition is used to inform the process of memory clipping (this is only for Android Java layer APP). This operation usually occurs when opening a new Activity screen, returning to the background, switching to the APP, and so on. The updateOomAdjLocked code is about 600 lines long. Try to simplify as follows, or rather long, here divided into sections to comb:
final void updateOomAdjLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
<! -- key point 1: find TOP -- APP, TOP display APP-->final ProcessRecord TOP_APP = TOP_ACT ! = null ? TOP_ACT.app : null; final long oldTime = SystemClock.uptimeMillis() - ProcessList.MAX_EMPTY_TIME; mAdjSeq++; mNewNumServiceProcs = 0; final int emptyProcessLimit; final int hiddenProcessLimit;<! -- keypoint 2 find TOP -- APP, TOP display APP-->// Initialize some process limits: if (mProcessLimit)<= 0) {
emptyProcessLimit = hiddenProcessLimit = 0;
} else if (mProcessLimit= =1) {
emptyProcessLimit = 1;
hiddenProcessLimit = 0;
} else{// The ratio of the empty process to the background non-empty cache inheritanceemptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);
cachedProcessLimit = mProcessLimit - emptyProcessLimit;
}
<!--The key point3Determine the process slot3A slot->int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2; // background process/foreground process/empty process int numEmptyProcs = n-mNumNoncachedProcs -mNumCacheDhidDenProcs; int emptyFactor = numEmptyProcs/numSlots; if (emptyFactor< 1) emptyFactor = 1;
int hiddenFactor = (mNumHiddenProcs >0? mNumHiddenProcs : 1)/numSlots; if (hiddenFactor< 1) hiddenFactor = 1;
int stepHidden = 0;
int stepEmpty = 0;
int numHidden = 0;
int numEmpty = 0;
int numTrimming = 0;
mNumNonHiddenProcs = 0;
mNumHiddenProcs = 0;
int i = mLruProcesses.size();/ / priorityint curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;// Initialize some valuesint nextHiddenAdj = curHiddenAdj+1;/ / priorityint curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;/ / interestingint nextEmptyAdj = curEmptyAdj+2;Copy the code
The first three key points is mainly done some preparation work, key point 1 is pulled out TOP_APP alone, because it is more special, system is only a process, the day before yesterday the key 2 is mainly based on the current configuration cache access to the background process and limit the number of empty process, key points and 3 background process is divided into three backup, whether background process or empty process, The interinterpolation is evenly divided into 6 priorities. One priority can have multiple processes, and the priority of empty processes is not necessarily lower than HIDDEN process priority.
for (int i=N- 1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if(! app.killedByAm && app.thread ! =null) {
app.procStateChanged = false;
<! -> // computeOomAdjLocked computeOomAdjLocked process priorities, but not for background and empty processes computeOomAdjLocked The priorities are computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now) assigned by AMS itself according to the LRU principle; // The priority of some processes, such as those with only background activities or those without activities, <! -> if (app.curadj >= processList.unknown_adj) {switch (app.curprocState) {case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: app.curRawAdj = curCachedAdj; <! Assign priority to background processes based on LRUif (curCachedAdj ! = nextCachedAdj) { stepCached++; if (stepCached >= cachedFactor) { stepCached = 0; curCachedAdj = nextCachedAdj; nextCachedAdj += 2; if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; } } } break; default:<! Assign priority to background processes based on LRU
app.curRawAdj = curEmptyAdj;
app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += 2;
if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
}
}
}
break;
}
}
<! -- Key 8 set priority -->
applyOomAdjLocked(app, true, now, nowElapsed);Copy the code
The key points above are mainly to calculate the priority of all processes such as oOM_adj. For non-background processes, such as HOME process server process, backup process, etc., there is a unique calculation method, and the rest of the background processes are assigned priority according to LRU level 3.
<! Key points -9AMS selectively kills processes according to the number of cached processes, too many background processes -->switch (app.curProcState) {
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
if (numCached > cachedProcessLimit) {
app.kill("cached #" + numCached, true);
}
break;
case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > ProcessList.TRIM_EMPTY_APPS
&& app.lastActivityTime < oldTime) {
app.kill("empty for "
+ ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
/ 1000) + "s".true);
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
app.kill("empty #" + numEmpty, true); }}break;
default:
mNumNonCachedProcs++;
break; } <! Key points -10Count the number of clipping processes -->if(app.curProcState >= ActivityManager.PROCESS_STATE_HOME && ! app.killedByAm) {// All processes higher than home need to be clipped, excluding those with higher levelsnumTrimming++; }}}Copy the code
The two key points above are to see if the current daemons are too many or too old, and if there are too many or too old daemons, AMS has the right to kill them. And then there’s the pruning of survival progression that we care about:
final int numCachedAndEmpty = numCached + numEmpty;
int memFactor;
<! Android allows a certain number of background processes to exist, and only when there is insufficient memory, the background process will be reduced.
if (numCached <= ProcessList.TRIM_CACHED_APPS
&& numEmpty< =ProcessList.TRIM_EMPTY_APPS){// Level, the more powerful the kill, the less, need to be about the emergency time to killif (numCachedAndEmpty< =ProcessList.TRIM_CRITICAL_THRESHOLD){/ /3
memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
} else if (numCachedAndEmpty< =ProcessList.TRIM_LOW_THRESHOLD) { //5
memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
} else {
memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;}}else{// Sufficient number of background processes indicates sufficient memorymemFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
}
<!--The key point12The memory is trimmed according to the memory trimming levelAndroidWhen the background process is considered insufficient, memory is also insufficient-->if (memFactor ! = ProcessStats.ADJ_MEM_FACTOR_NORMAL) { if (mLowRamStartTime == 0) { mLowRamStartTime = now; } int step = 0; int fgTrimLevel; Switch (memFactor) {case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; break; case ProcessStats.ADJ_MEM_FACTOR_LOW: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; break; default: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; break; } int factor = numTrimming/3; int minFactor = 2; if (mHomeProcess ! = null) minFactor++; if (mPreviousProcess ! = null) minFactor++; if (factor< minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;Copy the code
Key point 11 is not easy to understand here: The Android system determines the current system memory status according to the number of background processes. The more background processes there are, the more memory is not tight; the less background processes there are, the more tight the memory is and the higher the reclamation level is. If the number of background processes is large, Memory tailoring is more lenient: processStats.adj_mem_factor_normal. If not, it is graded according to the number of caches. In 6.0 source code:
- If the number of background processes (including empty processes) is less than 3, memory is very tight, and the memory pruning factor is processStats.ADJ_mem_factor_critical
- If the number of background processes (including empty ones) is less than 5, memory is very tight, and the memory pruning factor is processStats.ADJ_mem_factor_low
- If there are more than two but still less than the normal number of backgrounds, the memory clipping factor is ProcessStats.ADJ_MEM_FACTOR_MODERATE
The corresponding key point 12 is to establish the clipping level of the foreground RUNNING process (not necessarily the foreground display).
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
- ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
After that, we really started cutting APP. Here we first look at the cutting of background process shortage. This part is relatively complicated:
<! -- Clipping background process -->for (int i=N- 1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
// PROCESS_STATE_HOME = 12;
//PROCESS_STATE_LAST_ACTIVITY = 13; The ones that are back in the background will use it
TRIM_MEMORY_COMPLETE componentCallbacks2. TRIM_MEMORY_COMPLETE
// curProcState > 12 and am not killed; App.killedbyam is set in kill mode
// Clipping, if >= activityManager.process_state_HOME, the older clipping level is higher and less important, and the fresher the process, the lower the clipping level
if(app.curProcState >= ActivityManager.PROCESS_STATE_HOME && ! app.killedByAm) {// Take care of the oldest first
if(app.trimMemoryLevel < curLevel && app.thread ! =null) {
try {
app.thread.scheduleTrimMemory(curLevel);
} catch (RemoteException e) {
}
}
app.trimMemoryLevel = curLevel;
step++;
// There are only three slots in total, so when you refresh it again in the future, you need to check whether you are moving from one slot to another.
// If there is no movement, there is no need to crop again
if (step >= factor) {
step = 0;
switch (curLevel) {
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
break;
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
break; }}}Copy the code
App.curprocstate >= ActivityManager.PROCESS_STATE_HOME This department is mainly based on LRU to determine the different reduction levels.
else {
if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|| app.systemNoUi) && app.pendingUiClean) {
/ / release the UI
final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
if(app.trimMemoryLevel < level && app.thread ! =null) {
try {
app.thread.scheduleTrimMemory(level);
} catch (RemoteException e) {
}
}
app.pendingUiClean = false;
}
TrimMemoryLevel =0 when the APP is started, if necessary
if(app.trimMemoryLevel < fgTrimLevel && app.thread ! =null) {
try {
app.thread.scheduleTrimMemory(fgTrimLevel);
} catch(RemoteException e) { } } app.trimMemoryLevel = fgTrimLevel; }}}Copy the code
The clipping here is mainly for some processes with higher priority, whose clipping is usually ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN. Because this part of the process is more important, the clipping level is lower. If app.pendingUiClean==false, only the current process will be clipped:
else{<! Key points -13Clipping process when there is enough memory -->... for (int i=N- 1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
App.pendinguiclean ==true if the resume is set to true, app.pendinguiclean ==true
// So the branch decrement is done once, but not again
// Unless you go to the background process above, at which point the level of the process is already very low,
if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|| app.systemNoUi) && app.pendingUiClean) {
if(app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN && app.thread ! =null) {
try {
app.thread.scheduleTrimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
} catch (RemoteException e) {
}
}
// clean is false once
app.pendingUiClean = false;
}
// Do you have any problem with that
app.trimMemoryLevel = 0; }}}Copy the code
App.curprocstate >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND The clipping level is lower than app.curprocState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND. TRIM_MEMORY_UI_HIDDEN, so the APP can roughly know the current memory status of the system according to the cutting level, and also know how the system hopes to cut itself, and then the APP can make the corresponding slim. However, the above process tailoring priority is completely according to the number of background processes, however, different ROM can is reformed, so cut level may not entirely accurate, such as in the developer mode to open the option to limit the number of background process, limit the number of background process does not exceed 2, so this time clipping level is not very reasonable, Memory may be plentiful, but the clipping level is too high due to the limited number of background processes. Therefore, in the use of time, the best combination of cutting grade and the current amount of memory to comprehensive consideration.
Increase OOM_ADJ by rogue means, reduce the risk of being killed, and become rogue progression
Android has its own rules for calculating process priorities, even if some special scenarios require extra processing. But that has no binding effect on a rascal. Rogues can still follow the oOM_ADJ calculation rules and exploit its vulnerabilities to increase the oOM_ADJ of a process to reduce the risk of being killed. If only to reduce the risk of being killed, I am afraid that the APP that does not want to die but also wants to occupy resources will lead to insufficient memory in the system and cause the whole system to be stuck.
The calculation logic of priority is complicated. Here we will briefly describe the non-cached process, because once it becomes a cached process, its priority can only be calculated by LRU and cannot be controlled. A rogue does not allow itself to become a cached process. An uncached process is one of the following, and the higher the priority (the smaller the value), the harder it is to kill:
ADJ priority | priority | The process type |
---|---|---|
SERVICE_ADJ | 5 | Service Process |
HEAVY_WEIGHT_APP_ADJ | 4 | Background heavyweight process, set in the system/rootdir/init.rc file |
BACKUP_APP_ADJ | 3 | Backup process (unknown) |
PERCEPTIBLE_APP_ADJ | 2 | Can sense the process, such as background music playback, through startForeground set process |
VISIBLE_APP_ADJ | 1 | Visible process (Visible, but does not get focus, e.g. the new process has only one floating Activity, and the process behind it is Visible Process) |
FOREGROUND_APP_ADJ | 0 | Foreground process (APP, Foreground process, Foreground interface) |
- The first is raised to FOREGROUND_APP_ADJ
Let’s look at the process from bottom to TOP: how to program FOREGROUND_APP_ADJ, also known as the foreground process. There is no other way to program FOREGROUND_APP_ADJ. Only the TOP activity process can be considered a foreground process. Under normal interaction logic, this cannot be realized. It is possible to start an Activity when the screen is locked, but it needs to be hidden when the screen is lit up, which is easy to be perceived by the user. Therefore, there is basically no solution. Adding a View to the main screen via WindowManager does not prevent the process from being killed. Whether or not the process package is implemented through a pixel has not been answered personally.
- The second one is improved to VISIBLE_APP_ADJ or PERCEPTIBLE_APP_ADJ (different version levels may be different “4.3 = PERCEPTIBLE_APP_ADJ” and “> 5.0 = VISIBLE_APP_ADJ”). In addition, this method is generally difficult to kill APP, even if removed from the latest task list, in fact, the process is not killed, only killed the Activity and other components.
VISIBLE_APP_ADJ is a process that contains visible but non-interactive activities, while VIStible_APP_adj is a process that users are aware of, such as background music playback
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
static final int PERCEPTIBLE_APP_ADJ = 2;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
static final int VISIBLE_APP_ADJ = 1;Copy the code
This approach is relatively mild, and Android officials have offered similar solutions, such as setting up a foreground service to keep alive after music plays. This provides an entry point for rogue processes, but displaying a resident service will have a status icon in the notification bar, which will be perceived by the user. But Android also has a bug that can remove the icon, so I don’t know if Google is doing this on purpose. For details, see wechat’s live preservation scheme: Dual Services Force foreground processes to live.
StartForeground Service (ID, new Notification()); Service foreground Service can be changed into foreground Service even if the process is pushed back to background, its priority can only be reduced to PERCEPTIBLE_APP_ADJ or VISIBLE_APP_ADJ, Generally will not be killed, there is a leak of the Android, if two Service with the same ID is set to the foreground process, and one through stopForeground cancelled at the front desk, according to the result is reserved a front desk Service, but not in the status bar display notice, so you will not be user perceived play rascal, This means is more commonly used rogue means. With this increase in priority, AMS killBackgroundProcesses can no longer kill processes. It will only kill processes whose OOM_adj is greater than processList.service_adj, and the most recent task list will only empty the Activity, not kill the process. Because the background APP priority has been improved to the PERCEPTIBLE_APP_ADJ or ProcessList.VISIBLE_APP_ADJ, it can be described as the worst rogue, if it still occupy the memory does not release, it is a rascals, here there is a remaining question: StartForeground can only be improved to PERCEPTIBLE_APP_ADJ, but it was improved to VISIBLE_APP_ADJ in the version after 5.0. Specific practices are as follows:
public class RogueBackGroundService extends Service {
private static int ROGUE_ID = 1;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, RogueIntentService.class);
startService(intent);
startForeground(ROGUE_ID, new Notification());
}
public static class RogueIntentService extends IntentService {
// Hooligans wake up Service
public RogueIntentService(String name) {
super(name);
}
public RogueIntentService() {
super("RogueIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
}
@Override
public void onCreate() {
super.onCreate();
startForeground(ROGUE_ID, new Notification());
}
@Override
public void onDestroy() {
stopForeground(true);// It will stop automatically
super.onDestroy(); }}}Copy the code
However, this vulnerability is disabled after Android7.1, because Google added a check: If the Service still has the same ID Notification bound by setForeground, it cannot cancelNotification, that is, the Notification will still be displayed (in the Notification list).
private void cancelForegroudNotificationLocked(ServiceRecord r) {
if(r.foregroundId ! =0) {
// First check to see if this app has any other active foreground services
// with the same notification ID. If so, we shouldn't actually cancel it,
// because that would wipe away the notification that still needs to be shown
// due the other service.
ServiceMap sm = getServiceMap(r.userId);
if(sm ! =null) {<! -- Check if the Service bound to the ID notification is undisplayed -->for (int i = sm.mServicesByName.size()- 1; i >= 0; i--) {
ServiceRecord other = sm.mServicesByName.valueAt(i);
if(other ! = r && other.foregroundId == r.foregroundId && other.packageName.equals(r.packageName)) {// Found one! Abort the cancel.<! If you find a Service that is still displayed, return it directly -->return; } } } r.cancelNotification(); }}Copy the code
In 7.1, wechat in the Google PlayStore channel seems to have abandoned this method of survival, because 7.1 wechat deleted from the latest task list can kill the process, if the above method is not killed.
Dual Service daemons live (this is also very rogue, but if it does not increase the priority (allows killing), it is a little conscience)
Android Binder obituaries will be sent to the Client if the Service Binder entity dies. The obituaries provide a loophole for the process to survive. Two binder services can be started in two processes that are C/S for each other. If one process dies, the other process receives an obituary, and when the obituary is received, the killed process is evoked. The logic is as follows:
Write two binder entity services PairServiceA, PairServiceB, and bind to each other at onCreate and again at onServiceDisconnected when obituary is received.
public class PairServiceA extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new AliveBinder();
}
@Override
public void onCreate() {
super.onCreate();
bindService(new Intent(PairServiceA.this, PairServiceB.class), mServiceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
bindService(new Intent(PairServiceA.this, PairServiceB.class), mServiceConnection, BIND_AUTO_CREATE);
ToastUtil.show("bind A"); }};Copy the code
It’s paired with B
public class PairServiceB extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new AliveBinder();
}
@Override
public void onCreate() {
super.onCreate();
bindService(new Intent(PairServiceB.this, PairServiceA.class), mServiceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
bindService(newIntent(PairServiceB.this, PairServiceA.class), mServiceConnection, BIND_AUTO_CREATE); }}; }Copy the code
Then register in the Manifest, be careful to separate the processes
<service android:name=".service.alive.PairServiceA"/>
<service
android:name=".service.alive.PairServiceB"
android:process=":alive"/>Copy the code
Then start a Service in Application or Activity.
startService(new Intent(MainActivity.this, PairServiceA.class));Copy the code
This usually works because Binder obituaries come with the Binder framework, unless all parent processes are killed at once, which has not been tested.
The broadcast or Service in-place resurrection process survives
Other common ways to keep a process alive are to register a BroadcastReceiver. For example:
- boot
- Network State Switching
- The camera
- Some domestic push SDKS (including some)
If you want to start a Service (if you want to start a Service (if you want to start a Service (if you want to start a Service (if you want to start a Service) if you want to start a Service (if you want to start a Service) if you want to start a Service (if you want to start a Service) if you want to start a Service (if you want to start a Service) if you want to start a Service (if you want to start a Service)
public class BackGroundService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
returnSTART_STICKY; }}Copy the code
conclusion
All rogue means of the process of survival, are the worst policy, do not use, this paper is only the analysis of the experiment. When the APP returns to the background and the priority becomes low, it should timely release the memory to improve the system smoothness. Relying on rogue means to improve the priority, but not releasing the memory, keeping the immortality, is dead.
Android background Kill (FragmentActivity, PhoneWindow) LowMemoryKiller (4.3-6.0) Binder Obituaries
For reference only, welcome correction
Reference documentation
Android 7.0 ActivityManagerService(8) Process Management (2) updateOomAdjLocked Android 7.0 ActivityManagerService(9) Process management related process analysis (3) computeOomAdjLocked Fine Android code memory optimization recommendations -OnTrimMemory optimization fine wechat Android client background live experience sharing Android LowMemory Killer mechanism applies memory optimization OnLowMemory&OnTrimMemory