About the author

Guo Xiaoxing, programmer and guitarist, is mainly engaged in the infrastructure of Android platform. Welcome to exchange technical questions. You can go to my Github to raise an issue or send an email to [email protected] to communicate with me.

The article directories

  • A process creation and startup process
  • The priority of the process
  • Three process scheduling flow

The startup process of The Android system is shown below (click to view the larger picture) :

Loader layer

  1. When the phone is in the shutdown state, long press the power button to power up, the Boot chip starts to execute from the preset code solidified in the Boot ROM, and then loads the Boot program Boot Loader into RAM.
  2. After the Boot Loader is loaded into the RAM, the program mainly completes the functions of checking THE RAM and initializing hardware parameters.

The Kernel layer

  1. After the boot program enters the Android kernel layer, start the Swapper process (Idle process) first, which is used to initialize process management, memory management, load Display, Camera Driver, Binder Driver and other related work.
  2. After the swapper process is started, the kthreadd process creates kernel daemons such as kernel worker thread kworkder, soft interrupt thread ksoftirqd, and Thernal. The Kthreadd process is the ancestor of all kernel processes.

Native layer

  1. Init, the ancestor of all user processes, incubates user daemons such as Ueventd, Logd, HealthD, Installd, ADBD, and LMKD, and starts ServiceManager to manage system services. Start Bootnaim boot animation.
  2. The Zygote process is generated by parsing init.rc file fork. The Zygote process is the first Java process in Android system. It is the parent process of all Java processes. Load a VM. Preloading Class; Preload Resources.

The Framework layer

  1. The init process then forks the Media Server process, which is responsible for starting and managing the entire C++ Framwork (including AudioFlinger, Camera Service, etc.).

  2. The Zygote process then forks the System Server process, which is responsible for starting and managing the entire Java Framwork (including ActivityManagerService, WindowManagerService, and so on).

The App layer

The first application that the Zygote process hatches is the Launcher process. It also hatches the Browser process, Phone process, and so on. Each application we create is a separate process.

Through the analysis of the above process, the reader must have a general understanding of the entire Process model of Android. As an application developer we tend to pay more attention to the Framework layer and App layer process creation and management related principles, let’s analyze one by one.

A process creation and startup process

Before we formally introduce process, let’s think about the question, what is process, what is the nature of process? 🤔

As we know, the code is static, and the system composed of code and resources needs a dynamic existence if it wants to run. The process is the dynamic execution process of the program. What is process? A process is a collection of code and related resources that handle the execution state, including code side segments, files, signals, CPU state, memory address space, and so on.

Processes are described using the task_struct structure, as follows:

  • Code snippet: instructions formed after compilation
  • Data segment: Data required by the program to run
    • Read-only data segment: constant
    • Initialized data segment: global variable, static variable
    • Uninitialized Data segment (BSS) : Uninitialized global and static variables
  • Stack segment: Some memory dynamically allocated while a program is running
  • PCB: process information, status identification, etc

For more detailed information on processes, check out the Linux books, but to give you an overview, we’ll focus on processes and Android applications.

At the beginning of this article, we mentioned the various processes running on the system. How are these processes created? 🤔

Let’s take a look at how the most familiar application processes are created. We have already said that each application runs in a separate process. When ActivityManagerService starts four components, it creates a new process if the component’s process is not started. The timing of the startup process is also discussed in the analysis of the startup process of the four components. Here is a summary:

  • Activity ActivityStackSupervisor.startSpecificActivityLocked()
  • Service ActiveServices.bringUpServiceLocked()
  • ContentProvider ActivityManagerService.getContentProviderImpl() = Broadcast BroadcastQueue.processNextBroadcast()

The zygote process is created by copying itself. The new process starts with a Binder thread pool and a message loop, as shown below:

  1. When we click the app icon to launch the app or start an Activity with the Process tag within the app, a request to create a new process is triggered by Binder to the system_server process. That is, to ActivityManagerService for processing.
  2. The system_server Process calls the process.start () method, which collects parameters such as uid and GID, and sends them to Zygote via Socket to create a new Process.
  3. The Zygote process receives the request to create a new process, calls the zygoteinit.main () method for the runSelectLoop() body, executes the Zygoteconnection.runonce () method when there is a client connection, and forks the new application process.
  4. The newly created process calls the handleChildProc() method and finally the familiar ActivityThread.main() method.

Note: The whole process will involve two process communication methods, Binder and Socket, which will be analyzed separately in a special article in the future. This will not be expanded.

This is the outline of the process. Let’s take a look at the code implementation, starting with a process startup sequence diagram:

From the first step to the third step, collect a series of parameters such as UID, GID, groups, target-SDK, and nice-name to prepare for the subsequent startup of a new process. The openZygoteSocketIfNeeded() method is then called to open Socket communication, issuing a request to the Zygote process to create a new process.

Note: The process.start () method in step 2 is a blocking operation that waits until the Process is created and returns the PID before completing the method.

Let’s focus on a few key functions.

1.1 Process. OpenZygoteSocketIfNeeded (String) abi

How does the Process class communicate with the Zygote Process? 🤔

The static inner class ZygoteState of Process has a member variable LocalSocket object that connects to the member variable LocalServerSocket object of the ZygoteInit class, as shown below:

The client

public static class ZygoteState {
    final LocalSocket socket;
}
Copy the code

The service side

public class ZygoteInit {
    // The Socket is bound to the /dev/socket/zygote file
    private static LocalServerSocket sServerSocket;
}
Copy the code

Let’s look at the implementation in the code.

 public static class ZygoteState {
    
    public static ZygoteState connect(String socketAddress) throws IOException {
        DataInputStream zygoteInputStream = null;
        BufferedWriter zygoteWriter = null;
        // Create the LocalSocket object
        final LocalSocket zygoteSocket = new LocalSocket();

        try {
            // Connect LocalSocket to LocalServerSocket
            The //LocalSocket object looks for a file named "zygote" in the /dev/socket directory
            // Then bind yourself to it, thus establishing the connection.
            zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                    LocalSocketAddress.Namespace.RESERVED));

            // Create an input stream for LocalSocket to receive data from Zygote
            zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

            // Create an output stream of LocalSocket so that data can be sent to Zygote.
            zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                    zygoteSocket.getOutputStream()), 256);
        } catch (IOException ex) {
            try {
                zygoteSocket.close();
            } catch (IOException ignore) {
            }

            throw ex;
        }

        String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
        Log.i("Zygote"."Process: zygote socket opened, supported ABIS: " + abiListString);

        return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                Arrays.asList(abiListString.split(","))); }}Copy the code

The process of establishing a Socket connection is clear, as follows:

  1. Create the LocalSocket object.
  2. Connect LocalSocket to LocalServerSocket. The LocalSocket object looks up a file named “zygote” in the /dev/socket directory and binds itself to it.
  3. Create an input stream for LocalSocket to receive data from the Zygote process.
  4. Create an output stream of LocalSocket so that you can send data to the Zygote process.

1.2 ZygoteInit. Main (String argv [])

ZygoteInit is a startup class for the Zygote process. This class preloads some classes and then starts a loop, waiting for the command to create a new process to be sent through the Socket, and forking out a new child process.

The entry function to ZygoteInit is the main() method, as follows:

public class ZygoteInit {
    
    public static void main(String argv[]) {
            // Mark zygote start. This ensures that thread creation will throw
            // an error.
            ZygoteHooks.startZygoteNoThreadCreation();
    
            try {
                / /...
                registerZygoteSocket(socketName);
                / /...
                // Start the loop
                runSelectLoop(abiList);
    
                closeServerSocket();
            } catch (MethodAndArgsCaller caller) {
                caller.run();
            } catch (Throwable ex) {
                Log.e(TAG, "Zygote died with exception", ex);
                closeServerSocket();
                throwex; }}// Start a selection loop to receive commands sent through the Socket to create a new thread
    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        //sServerSocket refers to the server for Socket communication and has an index of 0 in FDS
        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        // Start the loop
        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                // Process the polling state, and proceed down when pollFds has time to come, otherwise block here.
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                
                // The IO multiplexing mechanism is used when the connection request is received from the client or when the data processing request arrives
                // Execute down, otherwise enter continue to break the loop.
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                // If the index is 0, that is, sServerSocket, it indicates that the connection request from the client is received.
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } 
                // If the index is not 0, it indicates that the Socket receives data from the peer end and performs corresponding operations.
                else {
                    boolean done = peers.get(i).runOnce();
                    // Remove the corresponding file descriptor after processing is complete.
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }
}
Copy the code

ZygoteInit starts the loop by calling runSelectLoop() in its entry function main() to receive requests from the Socket. Requests fall into two categories:

  1. Connection request
  2. Data request

When there is no connection request, the Zygote process will go to sleep. When there is a connection request, the Zygote process will wake up and call the acceptCommadPeer() method to create the Socket channel ZygoteConnection

private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex); }}Copy the code

The runOnce() method is then called to read the data in the connection request and create a new process.

In addition, the connect() operation received by the server to the client performs the ACCPET () operation to establish the connection hand. The client writes data through write() and the server reads data through read().

1.3 ZygoteConnection. RunOnce ()

class ZygoteConnection {
    
    boolean runOnce(a) throws ZygoteInit.MethodAndArgsCaller {
    
            String args[];
            Arguments parsedArgs = null;
            FileDescriptor[] descriptors;
    
            try {
                // Read the list of parameters sent by the client
                args = readArgumentList();
                descriptors = mSocket.getAncillaryFileDescriptors();
            } catch (IOException ex) {
                Log.w(TAG, "IOException on command socket " + ex.getMessage());
                closeSocket();
                return true;
            }
    
            / /... Processing parameters
    
            try {
                
                / /... Processing parameters
    
    
                // Call Zygote. Forkandwte (fork the new process
                pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                        parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                        parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                        parsedArgs.appDataDir);
            } catch (ErrnoException ex) {
                logAndPrintError(newStderr, "Exception creating pipe", ex);
            } catch (IllegalArgumentException ex) {
                logAndPrintError(newStderr, "Invalid zygote arguments", ex);
            } catch (ZygoteSecurityException ex) {
                logAndPrintError(newStderr,
                        "Zygote security policy prevents request: ", ex);
            }
    
            try {
                //pid == 0 indicates that the newly created child process is executing heavily
                if (pid == 0) {
                    // in child
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
                    handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
    
                    // should never get here, the child is expected to either
                    // throw ZygoteInit.MethodAndArgsCaller or exec().
                    return true;
                } 
                // pid < 0 indicates that the process fails to be created. Pid > 0 indicates that the process is being executed in the parent process
                else {
                    // in parent... pid of < 0 means failure
                    IoUtils.closeQuietly(childPipeFd);
                    childPipeFd = null;
                    returnhandleParentProc(pid, descriptors, serverPipeFd, parsedArgs); }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}}Copy the code

Process of this method is mainly used to read boot parameters, then calls the Zygote. ForkAndSpecialize new process fork () method, this method is the core of the process of creating a new method, it can call in succession three main methods to get the job done:

  1. PreFork () : Stops the Zygote four Daemon child threads and initializes the GC heap. The four Daemon child threads are the Java heap memory Management field, the heap reference queue thread, the destructor thread, and the monitor thread.
  2. NativeForkAndSpecialize () : Calls the Linux system function fork() to create a new process, create a thread pool for Java heap processing, reset GC performance data, set the signal processing function for the process, and start the JDWP thread.
  3. PostForkCommon () : Starts four Daemon child threads of the previously stopped Zygote process.

After all the above methods are done, the new process is created and returns pid, and handleChildProc() is called to start the new process. The handleChildProc() method then calls runtimeinit.zygoteinit () to start the new process.

ZygoteInit (int targetSdkVersion, String[] argv, ClassLoader ClassLoader)

This is one of the key methods used to create some runtime environments. Let’s take a look.

public class RuntimeInit {
    
    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();
        // Create general information about the application process, such as time zone and keyboard
        commonInit();
        // Create a Binder thread pool in the application process
        nativeZygoteInit();
        // Create application informationapplicationInit(targetSdkVersion, argv, classLoader); }}Copy the code

This approach accomplishes three main things:

  1. Call the commonInit() method to create common information such as the time zone and keyboard for the application process.
  2. Call the nativeZygoteInit() method to create a Binder thread pool in the application process.
  3. Call the applicationInit(targetSdkVersion, argv, classLoader) method to create the application information.

Binder thread pools will be examined in future articles, focusing on the implementation of the applicationInit(targetSdkVersion, Argv, classLoader) method, which is used to create applications.

The argv argument in this method refers to the ActivityThread, which invokeStaticMain() calls the main() method of the ActivityThread class reflectively. As follows:

public class RuntimeInit {
    
      private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
              throws ZygoteInit.MethodAndArgsCaller { 
          / /...
  
          // Remaining arguments are passed to the start class's static main
          invokeStaticMain(args.startClass, args.startArgs, classLoader);
      }
      
      private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
              throws ZygoteInit.MethodAndArgsCaller { Class<? > cl;// Call the main() method of the ActivityThread class through reflection
          try {
              cl = Class.forName(className, true, classLoader);
          } catch (ClassNotFoundException ex) {
              throw new RuntimeException(
                      "Missing class when invoking static main " + className,
                      ex);
          }
  
          Method m;
          try {
              m = cl.getMethod("main".new Class[] { String[].class });
          } catch (NoSuchMethodException ex) {
              throw new RuntimeException(
                      "Missing static main on " + className, ex);
          } catch (SecurityException ex) {
              throw new RuntimeException(
                      "Problem getting static main on " + className, ex);
          }
          / /...}}Copy the code

We are familiar with the main() method of the ActivityThread class, where the main thread Looper is created and the message loop is opened, as shown below:

public final class ActivityThread {
    
   public static void main(String[] args) {
       / /...
       Environment.initForCurrentUser();
       / /...
       Process.setArgV0("<pre-initialized>");
       // Create the main thread looper
       Looper.prepareMainLooper();
   
       ActivityThread thread = new ActivityThread();
       //attach to system process
       thread.attach(false);
   
       if (sMainThreadHandler == null) {
           sMainThreadHandler = thread.getHandler();
       }
   
       // The main thread enters the loop state
       Looper.loop();
   
       throw new RuntimeException("Main thread loop unexpectedly exited"); }}Copy the code

Process.start() is used to create and start an application Process. If there is a start, there is an end. If there is a start, there is an end.

The priority of the process

Processes can be classified into real-time processes and common processes according to their priorities.

A smaller prio value indicates a higher process priority.

  • Static priority: The priority does not change over time and the kernel does not change. The nice value can only be changed through system calls. The priority mapping formula is: Static_prio = MAX_RT_PRIO + nice + 20 MAX_RT_PRIO = 100, 139. Corresponds to a normal process;
  • Real-time priority: the value range is [0, MAX_RT_PRIO -1], where MAX_RT_PRIO = 100, the value range is [0, 99]. Corresponding real-time process;
  • Love priority: The scheduler rewards IO – consuming processes or penalizes CPU – consuming processes by increasing or decreasing the process priority. Interval range [0, MX_PRIO-1], where MX_PRIO = 140, then the value range is [0,139].

Three process scheduling process

Process scheduling is done in the Process class.

3.1 Priority scheduling

Priority scheduling method

setThreadPriority(int tid, int priority)
Copy the code

The priority of the process and the corresponding NICE value are shown below:

  • THREAD_PRIORITY_LOWEST 19 Lowest priority
  • THREAD_PRIORITY_BACKGROUND 10 background
  • THREAD_PRIORITY_LESS_FAVORABLE 1 is slightly lower than the default
  • THREAD_PRIORITY_DEFAULT 0, the default
  • Thread_priority_more_consump-1 is slightly higher than the default
  • THREAD_PRIORITY_FOREGROUND – 2 the front desk
  • THREAD_PRIORITY_DISPLAY -4 Displays correlation
  • Thread_priority_urgent_display-8 Displays (more important), input event
  • Thread_priority_audio-16 Audio correlation
  • Thread_priority_urgent_audio-19

3.2 Group Priority Scheduling

Process group priority scheduling method

setProcessGroup(int pid, int group)
setThreadGroup(int tid, int group)
Copy the code

Group priority and corresponding value

  • Thread_group_default-1 is used only by setProcessGroup to promote processes with priority <=10 to -2
  • THREAD_GROUP_BG_NONINTERACTIVE 0 CPU time-sharing duration decreases
  • THREAD_GROUP_FOREGROUND 1 CPU time-sharing duration is normal
  • THREAD_GROUP_SYSTEM 2 System thread group
  • THREAD_GROUP_AUDIO_APP 3 Application audio
  • THREAD_GROUP_AUDIO_SYS 4 System program audio

3.3 Scheduling Policies

Scheduling policy setting method

setThreadScheduler(int tid, int policy, int priority)
Copy the code
  • SCHED_OTHER Default standard round-robin time-sharing policy
  • SCHED_BATCH Scheduling for scheduling policies for processes with the Batch style
  • SCHED_IDLE Idle schedules for very low priority processes suitable for running in the background
  • SCHED_FIFO real-time first-in, first-out scheduling policy, not yet implemented for Android
  • SCHED_RR circulates real-time scheduling policies that are not yet implemented for Android

3.4 Process adj Scheduling

In addition to these basic scheduling policies, the Android system defines two process-specific state values: adj in processList. Java and procState in ActivityManager.java.

Defined in processList. Java file, oom_adj has 16 levels ranging from -17 to 16.

  • UNKNOWN_ADJ 16 indicates that the process will be cached and cannot obtain a certain value
  • CACHED_APP_MAX_ADJ 15 Maximum value of ADJ for an invisible process 1
  • CACHED_APP_MIN_ADJ 9 Minimum value of adj for an invisible process 2
  • SERVICE_B_AD 8 services in B List (older, less likely to use)
  • PREVIOUS_APP_ADJ 7 Process of the previous App (usually by pressing the back key)
  • HOME_APP_ADJ 6 Home process
  • SERVICE_ADJ 5 Service Process
  • HEAVY_WEIGHT_APP_ADJ 4 Background heavyweight process, set in system/rootdir/init.rc
  • BACKUP_APP_ADJ 3 Backup process 3
  • PERCEPTIBLE_APP_ADJ 2 Can perceive processes such as background music playback 4
  • VISIBLE_APP_ADJ 1 Visible Process 5
  • FOREGROUND_APP_ADJ 0 Foreground Process 6
  • Persistent_service_adj-11 is associated with a system or persistent process
  • Persistent_proc_ad-12 Persistent process, such as Telephony
  • System_ad-16 System process
  • Native_ad-17 Native process (not managed by the system)

The methods for updating a process’s adj value are defined in ActivityManagerService as follows:

  • UpdateOomAdjLocked: Updates adj, returns false if the target process is empty or killed; Otherwise return true;
  • ComputeOomAdjLocked: Computes ADJ and returns the calculated RawAdj value.
  • ApplyOomAdjLocked: applyOomAdjLocked, false if the target process needs to be killed; Otherwise return true.

When will the process’s ADJ value be updated? 🤔

Activity

  • ActivityManagerService. RealStartActivityLocked: start the Activity
  • ActivityStack. ResumeTopActivityInnerLocked: Activity return stack
  • ActivityStack. FinishCurrentActivityLocked: end of the current Activity
  • ActivityStack. DestroyActivityLocked: destroy the current Activity

Service

  • ActiveServices. RealStartServiceLocked: start the service
  • ActiveServices. BindServiceLocked: binding service (only update the current app)
  • ActiveServices. UnbindServiceLocked: unbundling services (only update the current app)
  • ActiveServices. BringDownServiceLocked: the end of the service (only update the current app)
  • ActiveServices. SendServiceArgsLocked: in bringup or cleanup service procedure call (only update the current app)

BroadcastReceiver

  • A broadcast BroadcastQueue. ProcessNextBroadcast: processing
  • BroadcastQueue. ProcessCurBroadcastLocked: deal with the current broadcast
  • BroadcastQueue. DeliverToRegisteredReceiverLocked: distribution of registered broadcasting (only update the current app)

ContentProvider

  • ActivityManagerService. RemoveContentProvider: remove the provider
  • ActivityManagerService. PublishContentProviders: release the provider (only update the current app)
  • ActivityManagerService. GetContentProviderImpl: access to the provider (only update the current app)

In addition, Lowmemorykiller also releases processes in six levels (highlighted above), based on current memory availability:

  • CACHED_APP_MAX_ADJ
  • CACHED_APP_MIN_ADJ
  • BACKUP_APP_ADJ
  • PERCEPTIBLE_APP_ADJ
  • VISIBLE_APP_ADJ
  • FOREGROUND_APP_ADJ

Defined in the activityManager.java file, process_state is divided into 18 classes ranging from -1 to 16

  • PROCESS_STATE_CACHED_EMPTY 16 The process is in cached state and empty
  • PROCESS_STATE_CACHED_ACTIVITY_CLIENT 15 The PROCESS_STATE_CACHED_ACTIVITY_CLIENT process is in cached state and is the client process of another cached process that contains Activity
  • PROCESS_STATE_CACHED_ACTIVITY 14 The process is in cached state and contains Activity
  • PROCESS_STATE_LAST_ACTIVITY 13 Background process that has the last displayed Activity
  • PROCESS_STATE_HOME 12 Background process with home Activity
  • PROCESS_STATE_RECEIVER 11 Background process, and receiver is running
  • PROCESS_STATE_SERVICE 10 Background process, and Service is running
  • PROCESS_STATE_HEAVY_WEIGHT 9 Background process, but cannot execute restore, so try to avoid killing the process
  • PROCESS_STATE_BACKUP 8 Background process. The backup/restore operation is running
  • PROCESS_STATE_IMPORTANT_BACKGROUND 7 The process is important to the user and the user cannot be aware of its existence
  • PROCESS_STATE_IMPORTANT_FOREGROUND 6 Indicates the process that is important to the user and can be sensed by the user
  • PROCESS_STATE_TOP_SLEEPING 5 The same as PROCESS_STATE_TOP, but the device is sleeping
  • PROCESS_STATE_FOREGROUND_SERVICE 4 has a foreground Service
  • PROCESS_STATE_BOUND_FOREGROUND_SERVICE 3 Has a foreground Service and is bound by the system
  • PROCESS_STATE_TOP 2 has the top Activity visible to the current user
  • PROCESS_STATE_PERSISTENT_UI 1 Persistent System process that is performing UI operations
  • PROCESS_STATE_PERSISTENT 0 Persistent System process
  • PROCESS_STATE_NONEXISTENT -1 Indicates a non-existent process

According to the above description of adj. and state values, we can divide the process into five levels according to their importance:

Foreground process

Processes that are necessary for the user’s current operation. A process is considered a foreground process if it meets any of the following criteria:

  • Hosting the Activity the user is interacting with (the Activity’s onResume() method has been called)
  • Hosts a Service that is bound to the Activity the user is interacting with
  • Host Service running “foreground” (Service called startForeground())
  • Hosting a Service (onCreate(), onStart(), or onDestroy()) that is performing a lifecycle callback
  • Host the BroadcastReceiver that is executing its onReceive() method

Typically, there are not many foreground processes at any given time. The system terminates them only when it is absolutely necessary that there is not enough memory to support them while still running. At this point, the device is usually paging out of memory, so some foreground processes need to be terminated to ensure that the user interface responds properly.

Visible process

A process that does not have any foreground components but still affects what the user sees on the screen. A process is considered visible if it meets any of the following criteria:

  • Hosts an Activity (whose onPause() method has been called) that is not in the foreground but is still visible to the user. For example, this might happen if the foreground Activity starts a dialog box that allows the previous Activity to be displayed after it.
  • Hosts a Service bound to a visible (or foreground) Activity.

Visible processes are considered extremely important and will not be terminated unless necessary to keep all foreground processes running at the same time.

Service process

A process that is running a service started with the startService() method and does not belong to either of the higher categories of processes described above. Although server processes are not directly related to what the user sees, they are often performing operations that the user cares about (for example, playing music in the background or downloading data from the network). Therefore, unless there is not enough memory to keep all foreground and visible processes running at the same time, the system will leave the server processes running.

Background processes

The process that contains an Activity that is currently invisible to the user (the Activity’s onStop() method has been called). These processes have no direct impact on the user experience, and the system may terminate them at any time to reclaim memory for foreground, visible, or server processes. There are usually many background processes running, so they are saved in the LRU (Least Recently used) list to ensure that the process containing the Activity the user recently viewed is the last to terminate. If an Activity implements the lifecycle method correctly and saves its current state, terminating its process does not have a significant impact on the user experience, because the Activity resumes all its visible states when the user navigates back to the Activity.

An empty process

A process that does not contain any active application components. The sole purpose of keeping such processes is to be used as a cache to reduce the startup time needed to run components in it the next time. To balance overall system resources between the process cache and the underlying kernel cache, systems often kill these processes.