Is there a way (with root?) to prevent Android task killer from killing certain apps? I'd like a browser app (FireFox) not to be killed every now and then by the system's task killer since I'd like to use certain single page applications that hold some data and are not loaded very quickly. Is there any way to deal with this, even if root is required?

Some time ago, I've updated my device from a 1 GB RAM to another one, 3 GB RAM and everything went well (browser stopped to get killed at all). But after a system update in Jan 2016, things got even worse, now FireFox usually gets killed even after an alarm clock rang or I opened a camera app. This is a major issue for me so I appreciate any help about this.

PS yeap, I've tried Settings→Battery→Battery Optimization where I both added FF to exceptions (didn't help) and turned Battery Optimization at all (didn't help as well).
Tl;dr

It's possible with root, you can tweak oom_adj values to prevent apps from being killed, alternatively force the target app to stay in memory by "locking" it or change some related settings responsible for killing apps in low memory condition.



Background: Android RAM Management


  Android uses a different way of handling processes. Instead of killing
  every process after its activity ended, processes are kept until the
  system needs more memory. The idea is to give speed improvements if
  you start that activity again. But how/when does Android kill a
  process if it needs more memory and which process to kill first?
  
  This is managed by the LMK (Low Memory Killer) driver of Android. You
  may already know that every app/process in Android is assigned an
  oom_adj value, which indicates the likelihood of it being killed when
  an out of memory (OOM) situation occurs. More higher it's value, the
  higher likelihood of it getting killed. Valid range is -17 to +15. (if
  in the -17 range means it won't get killed). According to that, there
  are six groups (OOM groups), into which apps/processes are
  categorised:



Foreground app
Visible app
Secondary server
Hidden app
Content provider
Empty app


Basically these could be described as:


  FOREGROUND_APP:  This is the process running the current foreground
  app. We'd really  rather not kill it!
  
  VISIBLE_APP:  This is a process only hosting activities that are
  visible to the user, so we'd prefer they don't disappear. 
  
  SECONDARY_SERVER: This is a process holding a secondary server --
  killing it will not have much of an impact as far as the user is
  concerned. 
  
  HIDDEN_APP:  This is a process only hosting activities that
  are not visible, so it can be killed without any disruption. 
  
  CONTENT_PROVIDER: This is a process with a content provider that
  does not have any clients attached to it. If it did have any clients,
  its adjustment would be the  one for the highest-priority of those
  processes. 
  
  EMPTY_APP: This is a process without anything currently running in
  it. Definitely the first to go.


These groups are defined by oom_adj value limits, and apps would fall into one of those groups according to the oom_adj value assigned to that particular app. "Foreground apps" usually have an oom_adj value of 0 or less (so they are the least killable; i.e High priority). 

"Empty apps" have a higher oom_adj (they are killed early; i.e Low priority). Also, oom_adj value changes according to the state of the user app; it's 0 when the app is active in the foreground and assigned a higher value when the app goes to the background.


  Why their "killability" differ? Apps belonging to these different
  groups (that have different oom_adj's), start to get killed at
  different levels of free RAM. These triggering RAM limits are defined
  by the LMK minfree values. Above 6 categories correspond with 6 RAM
  limits which are set in the LMK minfree. eg: Stock Android 4.3 in my
  device comes with the minfree values of 48,60,72,84,96,120. (these
  are in MB).




Practically what it means is, Empty apps will get killed when ram goes below 120mb, Content providers when it goes below 96mb, Hidden apps when it goes below 84mb and so on.. lastly starts killing Foreground apps when ram goes below 48mb. (You may notice that this last value (48mb) is not desirable when using memory intensive apps like heavy games). 

NB:


In newer kernel, oom_ score _adj is used instead of old oom_adj. (oom_score_adj valid range is -1000 to 1000). But oom_adj is also maintained for perhaps compatibility.
It is said that there are many OOM process categories that are assigned different oom_adj priorities by the ActivityManagerService, but eventually all of those would be considered under above six slots/groups (according to oom_limits), for the purpose of killing by the LMK minfree triggers. Therefore, those six are the important ones for normal users.


We can check the minfree values (also change them) and see the OOM 
groupings of apps/processes with this Memory Manager app easily.

Not every device has the same OOM configuration.




Improving RAM Management

Nowadays there is much user friendly implementation of RAM management mechanism with UI in system settings (such as App optimisation in Battery settings, Protected apps in some ROMS e.g Huawei’s EMUI etc) and can have the same effect as described above:



Having said that we can now explore ways in which, RAM can be utilised without killing foreground applications too often. The target areas will include:

1.Adjusting minifree values to suit one’s needs

2.Locking apps in memory to prevent them from being killed

3.Overridding the hidden app limit of android




Adjusting minifree values


You can change the minfree values with memory manager and tap apply. (Additionally there is an option “apply at boot” and tick to preserve settings across boots). Several apps can achieve this e.g Memory manager, Minfree manager etc
Alternatively use terminal/adb using pages:

#!/system/bin/sh echo “values” &gt; /sys/module


Locking apps to stay in memory (making it resident)


a) Using App Settings (Xposed)


One simple method is used Xposed module, App settings (you need Xposed framework installed)
After installing the framework, then download the 'App Settings' module through Xposed installer.
Open App Settings and allow the applications to load, selected the target app (in this case firefox browser)
In the settings page, enable the toggle and activate editing mode, tick the “Resident” option and save. (Making apps resident, causes apps to staying memory without being killed and it is advised to use exit button in target application if it’s no longer needed otherwise it will deprive other apps memory)




b) Using memory locker

Another implementation of this concept is locking the target application in memory by controlling the system files for setting oom_adj for all running processes.

How the application works:


  Memory locker controls files in the /data directory and also controls
  sytem files in root for setting oom_adj for running processes. 
  
  All
  locked applications are automatically locked after each reboot.   Apps
  are classified as downloaded/system apps, to unlock select target app
  and click on the lock on right side. (You can optionally set oom_adj
  priority)



Overriding the hidden app limit


In addition the low memory killing mechanism, there is another parameter that controls killing of hidden and empty apps. Apps are killed when they go beyond specified limits. 
There is a build.prop setting that which can control this (default is usually a low number less than 25, and modifying this can increase this value to a much larger value like 80)
Add this line to build.prop file in /system and leave another blank line below that (be sure to make back up first)



ro.sys.fw.bg_apps_limit=80 



References and Credits


Android RAM Management (Special credits: mrhnet)
Memory Locker
Xposed - General info, versions &amp; changelog