Android進程框架:進程的建立、啓動與調度流程

關於做者java

郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。android

文章目錄git

  • 一 進程的建立與啓動流程
  • 二 進程的優先級
  • 三 進程的調度流程

Android系統的啓動流程以下圖(點擊查看大圖)所示:程序員

Loader層github

  1. 當手機處於關機狀態時,長按電源鍵開機,引導芯片開始從固化在Boot ROM裏的預設代碼開始執行,而後加載引導程序Boot Loader到RAM。
  2. Boot Loader被加載到RAM以後開始執行,該程序主要完成檢查RAM,初始化硬件參數等功能。

Kernel層瀏覽器

  1. 引導程序以後進入Android內核層,先啓動swapper進程(idle進程),該進程用來初始化進程管理、內存管理、加載Display、Camera Driver、Binder Driver等相關工做。
  2. swapper進程進程以後再啓動kthreadd進程,該進程會建立內核工做線程kworkder、軟中斷線程ksoftirqd、thernal等內核守護進程,kthreadd進程是全部內核進程的鼻祖。

Native層緩存

  1. 接着會啓動init進程,init進程是全部用戶進程的鼻祖,它會接着孵化出ueventd、logd、healthd、installd、adbd、lmkd等用戶守護進程,啓動ServiceManager來管理系統 服務,啓動Bootnaim開機動畫。
  2. init進程經過解析init.rc文件fork生成Zygote進程,該進程是Android系統第一個Java進程,它是全部Java進程父進程,該進程主要完成了加載ZygoteInit類,註冊Zygote Socket 服務套接字;加載虛擬機;預加載Class;預加載Resources。

Framework層網絡

  1. init進程接着fork生成Media Server進程,該進程負責啓動和管理整個C++ Framwork(包含AudioFlinger、Camera Service等服務)。架構

  2. Zygote進程接着會fork生成System Server進程,該進程負責啓動和管理整個Java Framwork(包含ActivityManagerService、WindowManagerService等服務)。app

App層

Zygote進程孵化出的第一個應用進程是Launcher進程(桌面),它還會孵化出Browser進程(瀏覽器)、Phone進程(電話)等。咱們每一個建立的應用都是一個單獨的進程。

經過上述流程的分析,想必讀者已經對Android的整個進程模型有了大體的理解。做爲一個應用開發者咱們每每更爲關注Framework層和App層裏進程的建立與管理相關原理,咱們來 一一分析。

一 進程的建立與啓動流程

在正式介紹進程以前,咱們來思考一個問題,何爲進程,進程的本質是什麼?🤔

咱們知道,代碼是靜態的,有代碼和資源組成的系統要想運行起來就須要一種動態的存在,進程就是程序的動態執行過程。何爲進程? 進程就是處理執行狀態的代碼以及相關資源的集合,包括代碼端段、文件、信號、CPU狀態、內存地址空間等。

進程使用task_struct結構體來描述,以下所示:

  • 代碼段:編譯後造成的一些指令
  • 數據段:程序運行時須要的數據
    • 只讀數據段:常量
    • 已初始化數據段:全局變量,靜態變量
    • 未初始化數據段(bss):未初始化的全局變量和靜態變量
  • 堆棧段:程序運行時動態分配的一些內存
  • PCB:進程信息,狀態標識等

關於進程的更多詳細信息,讀者能夠去翻閱Linux相關書籍,這裏只是給讀者帶來一種總體上的理解,咱們的重心仍是放在進程再Android平臺上的應用。

在文章開篇的時候,咱們提到了系統中運行的各類進程,那麼這些進程如何被建立呢?🤔

咱們先來看看咱們最熟悉的應用進程是如何被建立的,前面咱們已經說來每個應用都運行在一個單獨的進程裏,當ActivityManagerService去啓動四大組件時, 若是發現這個組件所在的進程沒有啓動,就會去建立一個新的進程,啓動進程的時機咱們在分析四大組件的啓動流程的時候也有講過,這裏再總結一下:

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

這個新進程就是zygote進程經過複製自身來建立的,新進程在啓動的過程當中還會建立一個Binder線程池(用來作進程通訊)和一個消息循環(用來作線程通訊) 整個流程以下圖所示:

  1. 當咱們點擊應用圖標啓動應用時或者在應用內啓動一個帶有process標籤的Activity時,都會觸發建立新進程的請求,這種請求會先經過Binder 發送給system_server進程,也便是發送給ActivityManagerService進行處理。
  2. system_server進程會調用Process.start()方法,會先收集uid、gid等參數,而後經過Socket方式發送給Zygote進程,請求建立新進程。
  3. Zygote進程接收到建立新進程的請求後,調用ZygoteInit.main()方法進行runSelectLoop()循環體內,當有客戶端鏈接時執行ZygoteConnection.runOnce() 方法,最後fork生成新的應用進程。
  4. 新建立的進程會調用handleChildProc()方法,最後調用咱們很是熟悉的ActivityThread.main()方法。

注:整個流程會涉及Binder和Socket兩種進程通訊方式,這個咱們後續會有專門的文章單獨分析,這個就再也不展開。

整個流程大體就是這樣,咱們接着來看看具體的代碼實現,先來看一張進程啓動序列圖:

從第一步到第三步主要是收集整理uid、gid、groups、target-sdk、nice-name等一系列的參數,爲後續啓動新進程作準備。而後調用openZygoteSocketIfNeeded()方法 打開Socket通訊,向zygote進程發出建立新進程的請求。

注:第二步中的Process.start()方法是個阻塞操做,它會一直等待進程建立完畢,並返回pid纔會完成該方法。

咱們來重點關注幾個關鍵的函數。

1.1 Process.openZygoteSocketIfNeeded(String abi)

關於Process類與Zygote進程的通訊是如何進行的呢?🤔

Process的靜態內部類ZygoteState有個成員變量LocalSocket對象,它會與ZygoteInit類的成員變量LocalServerSocket對象創建鏈接,以下所示:

客戶端

public static class ZygoteState {
    final LocalSocket socket;
}
複製代碼

服務端

public class ZygoteInit {
    //該Socket與/dev/socket/zygote文件綁定在一塊兒
    private static LocalServerSocket sServerSocket;
}
複製代碼

咱們來具體看看代碼裏的實現。

public static class ZygoteState {
    
    public static ZygoteState connect(String socketAddress) throws IOException {
        DataInputStream zygoteInputStream = null;
        BufferedWriter zygoteWriter = null;
        //建立LocalSocket對象
        final LocalSocket zygoteSocket = new LocalSocket();

        try {
            //將LocalSocket與LocalServerSocket創建鏈接,創建鏈接的過程就是
            //LocalSocket對象在/dev/socket目錄下查找一個名稱爲"zygote"的文件
            //而後將本身與其綁定起來,這樣就創建了鏈接。
            zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                    LocalSocketAddress.Namespace.RESERVED));

            //建立LocalSocket的輸入流,以即可以接收Zygote進程發送過來的數據
            zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

            //建立LocalSocket的輸出流,以即可以向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(",")));
    }
}
複製代碼

創建Socket鏈接的流程很明朗了,以下所示:

  1. 建立LocalSocket對象。
  2. 將LocalSocket與LocalServerSocket創建鏈接,創建鏈接的過程就是LocalSocket對象在/dev/socket目錄下查找一個名稱爲"zygote"的文件,而後將本身與其綁定起來,這樣就創建了鏈接。
  3. 建立LocalSocket的輸入流,以即可以接收Zygote進程發送過來的數據。
  4. 建立LocalSocket的輸出流,以即可以向Zygote進程發送數據。

1.2 ZygoteInit.main(String argv[])

ZygoteInit是Zygote進程的啓動類,該類會預加載一些類,而後便開啓一個循環,等待經過Socket發過來的建立新進程的命令,fork出新的 子進程。

ZygoteInit的入口函數就是main()方法,以下所示:

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);
                //...
                //開啓循環 
                runSelectLoop(abiList);
    
                closeServerSocket();
            } catch (MethodAndArgsCaller caller) {
                caller.run();
            } catch (Throwable ex) {
                Log.e(TAG, "Zygote died with exception", ex);
                closeServerSocket();
                throw ex;
            }
        }
    
    // 開啓一個選擇循環,接收經過Socket發過來的命令,建立新線程
    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        //sServerSocket指的是Socket通訊的服務端,在fds中的索引爲0
        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        //開啓循環
        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 {
                //處理輪詢狀態,當pollFds有時間到來時則往下執行,不然阻塞在這裏。
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                
                //採用IO多路複用機制,當接收到客戶端發出的鏈接請求時或者數據處理請求到來時則
                //往下執行,不然進入continue跳出本次循環。
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                //索引爲0,即爲sServerSocket,表示接收到客戶端發來的鏈接請求。
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } 
                //索引不爲0,表示經過Socket接收來自對端的數據,並執行相應的操做。
                else {
                    boolean done = peers.get(i).runOnce();
                    //處理完成後移除相應的文件描述符。
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }
}
複製代碼

能夠發現ZygoteInit在其入口函數main()方法裏調用runSelectLoop()開啓了循環,接收Socket發來的請求。請求分爲兩種:

  1. 鏈接請求
  2. 數據請求

沒有鏈接請求時Zygote進程會進入休眠狀態,當有鏈接請求到來時,Zygote進程會被喚醒,調用acceptCommadPeer()方法建立Socket通道ZygoteConnection

private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex);
    }
}
複製代碼

而後調用runOnce()方法讀取鏈接請求裏的數據,而後建立新進程。

此外,鏈接的過程當中服務端接受的到客戶端的connect()操做會執行accpet()操做,創建鏈接手,客戶端經過write()寫數據,服務端經過read()讀數據。

1.3 ZygoteConnection.runOnce()

class ZygoteConnection {
    
    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    
            String args[];
            Arguments parsedArgs = null;
            FileDescriptor[] descriptors;
    
            try {
                //讀取客戶端發過來的參數列表
                args = readArgumentList();
                descriptors = mSocket.getAncillaryFileDescriptors();
            } catch (IOException ex) {
                Log.w(TAG, "IOException on command socket " + ex.getMessage());
                closeSocket();
                return true;
            }
    
            //... 參數處理
    
            try {
                
                //... 參數處理
    
    
                //調用Zygote.forkAndSpecialize(來fork出新進程
                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時表示當前是在新建立的子進程重磅執行
                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表示建立新進程失敗,pid > 0 表示當前是在父進程中執行
                else {
                    // in parent...pid of < 0 means failure
                    IoUtils.closeQuietly(childPipeFd);
                    childPipeFd = null;
                    return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
                }
            } finally {
                IoUtils.closeQuietly(childPipeFd);
                IoUtils.closeQuietly(serverPipeFd);
            }
     }
}
複製代碼

該方法主要用來讀取進程啓動參數,而後調用Zygote.forkAndSpecialize()方法fork出新進程,該方法是建立新進程的核心方法,它主要會陸續調用三個 方法來完成工做:

  1. preFork():先中止Zygote進程的四個Daemon子線程的運行以及初始化GC堆。這四個Daemon子線程分別爲:Java堆內存管理現場、堆線下引用隊列線程、析構線程與監控線程。
  2. nativeForkAndSpecialize():調用Linux系統函數fork()建立新進程,建立Java堆處理的線程池,重置GC性能數據,設置進程的信號處理函數,啓動JDWP線程。
  3. postForkCommon():啓動以前中止的Zygote進程的四個Daemon子線程。

上面的方法都完成會後,新進程會建立完成,並返回pid,接着就調用handleChildProc()來啓動新進程。handleChildProc()方法會接着調用RuntimeInit.zygoteInit()來 完成新進程的啓動。

1.4 RuntimeInit.zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)

這個就是個關鍵的方法了,它主要用來建立一些運行時環境,咱們來看一看。

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();
        //建立應用進程的時區和鍵盤等通用信息
        commonInit();
        //在應用進程中建立一個Binder線程池
        nativeZygoteInit();
        //建立應用信息
        applicationInit(targetSdkVersion, argv, classLoader);
    }
}
複製代碼

該方法主要完成三件事:

  1. 調用commonInit()方法建立應用進程的時區和鍵盤等通用信息。
  2. 調用nativeZygoteInit()方法在應用進程中建立一個Binder線程池。
  3. 調用applicationInit(targetSdkVersion, argv, classLoader)方法建立應用信息。

Binder線程池咱們後續的文章會分析,咱們重點來看看applicationInit(targetSdkVersion, argv, classLoader)方法的實現,它主要用來完成應用的建立。

該方法裏的argv參數指的就是ActivityThread,該方法會調用invokeStaticMain()經過反射的方式調用ActivityThread類的main()方法。以下所示:

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;
          
          //經過反射調用ActivityThread類的main()方法
          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);
          }
          //...
      }  
}
複製代碼

走到ActivityThread類的main()方法,咱們就很熟悉了,咱們知道在main()方法裏,會建立主線程Looper,並開啓消息循環,以下所示:

public final class ActivityThread {
    
   public static void main(String[] args) {
       //...
       Environment.initForCurrentUser();
       //...
       Process.setArgV0("<pre-initialized>");
       //建立主線程looper
       Looper.prepareMainLooper();
   
       ActivityThread thread = new ActivityThread();
       //attach到系統進程
       thread.attach(false);
   
       if (sMainThreadHandler == null) {
           sMainThreadHandler = thread.getHandler();
       }
   
       //主線程進入循環狀態
       Looper.loop();
   
       throw new RuntimeException("Main thread loop unexpectedly exited");
   } 
}
複製代碼

前面咱們從Process.start()開始講起,分析了應用進程的建立及啓動流程,既然有啓動就會有結束,接下來咱們從 Process.killProcess()開始講起,繼續分析進程的結束流程。

二 進程的優先級

進程按照優先級大小不一樣又能夠分爲實時進程與普通進程。

prio值越小表示進程優先級越高,

  • 靜態優先級:優先級不會隨時間改變,內核也不會修改,只能經過系統調用改變nice值,優先級映射公式爲:static_prio = MAX_RT_PRIO + nice + 20,其中MAX_RT_PRIO = 100,那麼取值區間爲[100, 139];對應普通進程;
  • 實時優先級:取值區間爲[0, MAX_RT_PRIO -1],其中MAX_RT_PRIO = 100,那麼取值區間爲[0, 99];對應實時進程;
  • 懂愛優先級:調度程序經過增長或者減小進程優先級,來達到獎勵IO消耗型或按照懲罰CPU消耗型的進程的效果。區間範圍[0, MX_PRIO-1],其中MX_PRIO = 140,那麼取值區間爲[0,139];

三 進程調度流程

進程的調度在Process類裏完成。

3.1 優先級調度

優先級調度方法

setThreadPriority(int tid, int priority)
複製代碼

進程的優先級以及對應的nice值以下所示:

  • THREAD_PRIORITY_LOWEST 19 最低優先級
  • THREAD_PRIORITY_BACKGROUND 10 後臺
  • THREAD_PRIORITY_LESS_FAVORABLE 1 比默認略低
  • THREAD_PRIORITY_DEFAULT 0 默認
  • THREAD_PRIORITY_MORE_FAVORABLE -1 比默認略高
  • THREAD_PRIORITY_FOREGROUND -2 前臺
  • THREAD_PRIORITY_DISPLAY -4 顯示相關
  • THREAD_PRIORITY_URGENT_DISPLAY -8 顯示(更爲重要),input事件
  • THREAD_PRIORITY_AUDIO -16 音頻相關
  • THREAD_PRIORITY_URGENT_AUDIO -19 音頻(更爲重要)

3.2 組優先級調度

進程組優先級調度方法

setProcessGroup(int pid, int group)
setThreadGroup(int tid, int group)
複製代碼

組優先級及對應取值

  • THREAD_GROUP_DEFAULT -1 僅用於setProcessGroup,將優先級<=10的進程提高到-2
  • THREAD_GROUP_BG_NONINTERACTIVE 0 CPU分時的時長縮短
  • THREAD_GROUP_FOREGROUND 1 CPU分時的時長正常
  • THREAD_GROUP_SYSTEM 2 系統線程組
  • THREAD_GROUP_AUDIO_APP 3 應用程序音頻
  • THREAD_GROUP_AUDIO_SYS 4 系統程序音頻

3.3 調度策略

調度策略設置方法

setThreadScheduler(int tid, int policy, int priority)
複製代碼
  • SCHED_OTHER 默認 標準round-robin分時共享策略
  • SCHED_BATCH 批處理調度 針對具備batch風格(批處理)進程的調度策略
  • SCHED_IDLE 空閒調度 針對優先級很是低的適合在後臺運行的進程
  • SCHED_FIFO 先進先出 實時調度策略,android暫未實現
  • SCHED_RR 循環調度 實時調度策略,android暫未實現

3.4 進程adj調度

另外除了這些基本的調度策略,Android系統還定義了兩個和進程相關的狀態值,一個就是定義在ProcessList.java裏的adj值,另外一個 是定義在ActivityManager.java裏的procState值。

定義在ProcessList.java文件,oom_adj劃分爲16級,從-17到16之間取值。

  • UNKNOWN_ADJ 16 通常指將要會緩存進程,沒法獲取肯定值
  • CACHED_APP_MAX_ADJ 15 不可見進程的adj最大值 1
  • CACHED_APP_MIN_ADJ 9 不可見進程的adj最小值 2
  • SERVICE_B_AD 8 B List中的Service(較老的、使用可能性更小)
  • PREVIOUS_APP_ADJ 7 上一個App的進程(每每經過按返回鍵)
  • HOME_APP_ADJ 6 Home進程
  • SERVICE_ADJ 5 服務進程(Service process)
  • HEAVY_WEIGHT_APP_ADJ 4 後臺的重量級進程,system/rootdir/init.rc文件中設置
  • BACKUP_APP_ADJ 3 備份進程 3
  • PERCEPTIBLE_APP_ADJ 2 可感知進程,好比後臺音樂播放 4
  • VISIBLE_APP_ADJ 1 可見進程(Visible process) 5
  • FOREGROUND_APP_ADJ 0 前臺進程(Foreground process) 6
  • PERSISTENT_SERVICE_ADJ -11 關聯着系統或persistent進程
  • PERSISTENT_PROC_ADJ -12 系統persistent進程,好比telephony
  • SYSTEM_ADJ -16 系統進程
  • NATIVE_ADJ -17 native進程(不被系統管理)

更新進程adj值的方法定義在ActivityManagerService中,分別爲:

  • updateOomAdjLocked:更新adj,當目標進程爲空,或者被殺則返回false;不然返回true;
  • computeOomAdjLocked:計算adj,返回計算後RawAdj值;
  • applyOomAdjLocked:應用adj,當須要殺掉目標進程則返回false;不然返回true。

那麼進程的adj值何時會被更新呢?🤔

Activity

  • ActivityManagerService.realStartActivityLocked: 啓動Activity
  • ActivityStack.resumeTopActivityInnerLocked: 恢復棧頂Activity
  • ActivityStack.finishCurrentActivityLocked: 結束當前Activity
  • ActivityStack.destroyActivityLocked: 摧毀當前Activity

Service

  • ActiveServices.realStartServiceLocked: 啓動服務
  • ActiveServices.bindServiceLocked: 綁定服務(只更新當前app)
  • ActiveServices.unbindServiceLocked: 解綁服務 (只更新當前app)
  • ActiveServices.bringDownServiceLocked: 結束服務 (只更新當前app)
  • ActiveServices.sendServiceArgsLocked: 在bringup或則cleanup服務過程調用 (只更新當前app)

BroadcastReceiver

  • BroadcastQueue.processNextBroadcast: 處理下一個廣播
  • BroadcastQueue.processCurBroadcastLocked: 處理當前廣播
  • BroadcastQueue.deliverToRegisteredReceiverLocked: 分發已註冊的廣播 (只更新當前app)

ContentProvider

  • ActivityManagerService.removeContentProvider: 移除provider
  • ActivityManagerService.publishContentProviders: 發佈provider (只更新當前app)
  • ActivityManagerService.getContentProviderImpl: 獲取provider (只更新當前app)

另外,Lowmemorykiller也會根據當前的內存狀況逐級進行進程釋放,一共有六個級別(上面加粗的部分):

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

定義在ActivityManager.java文件,process_state劃分18類,從-1到16之間取值

  • PROCESS_STATE_CACHED_EMPTY 16 進程處於cached狀態,且爲空進程
  • PROCESS_STATE_CACHED_ACTIVITY_CLIENT 15 進程處於cached狀態,且爲另外一個cached進程(內含Activity)的client進程
  • PROCESS_STATE_CACHED_ACTIVITY 14 進程處於cached狀態,且內含Activity
  • PROCESS_STATE_LAST_ACTIVITY 13 後臺進程,且擁有上一次顯示的Activity
  • PROCESS_STATE_HOME 12 後臺進程,且擁有home Activity
  • PROCESS_STATE_RECEIVER 11 後臺進程,且正在運行receiver
  • PROCESS_STATE_SERVICE 10 後臺進程,且正在運行service
  • PROCESS_STATE_HEAVY_WEIGHT 9 後臺進程,但沒法執行restore,所以儘可能避免kill該進程
  • PROCESS_STATE_BACKUP 8 後臺進程,正在運行backup/restore操做
  • PROCESS_STATE_IMPORTANT_BACKGROUND 7 對用戶很重要的進程,用戶不可感知其存在
  • PROCESS_STATE_IMPORTANT_FOREGROUND 6 對用戶很重要的進程,用戶可感知其存在
  • PROCESS_STATE_TOP_SLEEPING 5 與PROCESS_STATE_TOP同樣,但此時設備正處於休眠狀態
  • PROCESS_STATE_FOREGROUND_SERVICE 4 擁有給一個前臺Service
  • PROCESS_STATE_BOUND_FOREGROUND_SERVICE 3 擁有給一個前臺Service,且由系統綁定
  • PROCESS_STATE_TOP 2 擁有當前用戶可見的top Activity
  • PROCESS_STATE_PERSISTENT_UI 1 persistent系統進程,並正在執行UI操做
  • PROCESS_STATE_PERSISTENT 0 persistent系統進程
  • PROCESS_STATE_NONEXISTENT -1 不存在的進程

根據上面說描述的adj值和state值,咱們又能夠按照重要性程度的不一樣,將進程劃分爲五級:

前臺進程

用戶當前操做所必需的進程。若是一個進程知足如下任一條件,即視爲前臺進程:

  • 託管用戶正在交互的 Activity(已調用 Activity 的 onResume() 方法)
  • 託管某個 Service,後者綁定到用戶正在交互的 Activity
  • 託管正在「前臺」運行的 Service(服務已調用 startForeground())
  • 託管正執行一個生命週期回調的 Service(onCreate()、onStart() 或 onDestroy())
  • 託管正執行其 onReceive() 方法的 BroadcastReceiver

一般,在任意給定時間前臺進程都爲數很少。只有在內存不足以支持它們同時繼續運行這一萬不得已的狀況下,系統纔會終止它們。 此時,設備每每已達到內存分頁狀態,所以須要終止一些前臺進程來確保用戶界面正常響應。

可見進程

沒有任何前臺組件、但仍會影響用戶在屏幕上所見內容的進程。 若是一個進程知足如下任一條件,即視爲可見進程:

  • 託管不在前臺、但仍對用戶可見的 Activity(已調用其 onPause() 方法)。例如,若是前臺 Activity 啓動了一個對話框,容許在其後顯示上一 Activity,則有可能會發生這種狀況。
  • 託管綁定到可見(或前臺)Activity 的 Service。

可見進程被視爲是極其重要的進程,除非爲了維持全部前臺進程同時運行而必須終止,不然系統不會終止這些進程。

服務進程

正在運行已使用 startService() 方法啓動的服務且不屬於上述兩個更高類別進程的進程。儘管服務進程與用戶所見內容沒有直接關聯,可是它們一般在執行一些用戶關 心的操做(例如,在後臺播放音樂或從網絡下載數據)。所以,除非內存不足以維持全部前臺進程和可見進程同時運行,不然系統會讓服務進程保持運行狀態。

後臺進程

包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop() 方法)。這些進程對用戶體驗沒有直接影響,系統可能隨時終止它們,以回收內存供前臺進程、可見進程或服務進程使用。 一般會有不少後臺進程在運行,所以它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最後一個被終止。若是某個 Activity 正確實現了生命週期方法,並保存了其當前狀態,則終止其進程不會對用戶體驗產生明顯影響,由於當用戶導航回該 Activity 時,Activity 會恢復其全部可見狀態。

空進程

不含任何活動應用組件的進程。保留這種進程的的惟一目的是用做緩存,以縮短下次在其中運行組件所需的啓動時間。 爲使整體系統資源在進程緩存和底層內核緩存之間保持平衡,系統每每會終止這些進程。

相關文章
相關標籤/搜索