Application.onCreate()會形成Service啓動ANR麼?

1 概述

本文針對Service啓動過程形成的ANR進行分析,同時啓動過程限制在如下兩個條件中:
(A進程,調用startService()方法的進程;B進程,須要開啓的Service所在的進程,A和B不是同一個進程)android

  1. 調用startService()啓動service,暫不分析bindService()的狀況
  2. 啓動Service前,B進程不存在

爲了分析ANR所產生的緣由,對於在不一樣進程中啓動Service的流程須要有一個簡單的瞭解,下面首先簡要分析一個這個過程。git

2 Service啓動流程

2.1 啓動流程時序圖

首先來看下簡化的啓動流程時序圖,共分爲兩張圖。第一張圖是描述startService()的調用過程,第二張圖是描述startProcess的調用過程。github

圖1:bash

圖2:app

2.2 流程分析

2.2.1 startService流程分析

從圖1中咱們已經能夠看到一個大體的流程,其中有一個對ActiveServices.bringUpServiceLocked()的調用,這個方法對分析啓動流程比較關鍵,下面咱們對這個方法進行一下分析。異步

private final String bringUpServiceLocked(...) throws TransactionTooLargeException {
    ...
    // 1. 若是Service已經啓動了,向B進程主線程發送消息,異步調用onStartCommand()方法
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(...);
    }
    ...
    // 2. 若是B進程存在,那麼向B進程發送消息,啓動Service
    app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
    if (app != null && app.thread != null) {
            realStartServiceLocked(...);
    }
    ...
    // 3. 若是B進程不存在,那麼首先建立進程,並將要啓動的Service添加到mPendingServices中。添加到mPendingServices中的Service,在進程建立成功後,會被依次啓動。
    if (app == null) {
        if ((app=mAm.startProcessLocked(...) == null) {
            ...
        }
        ...
    }
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    ...
}
複製代碼

由於本文分析的是在另一個未啓動的進程中啓動Service的狀況,因此咱們只需關注註釋3的流程。這個流程會先去異步的啓動B進程,同時將須要啓動的Service加到mPendingServices中。接下來咱們再去看看B進程啓動的流程。oop

2.2.2 startProcess流程分析

進程在啓動起來以後,會調用ActvityThread的main方法。這個方法主要作了兩件事:學習

  1. 告訴AMS進程已啓動:調用attach()方法
  2. 啓動Looper
2.2.2.1 ActivityThread.attach()方法解析

attach()方法在ActivityThread一側,最主要的是調用了AMS的attachApplication()方法,將自身進程的ApplicationThread對象發送給AMS。而後在AMS一側,AMS則作了如下操做:ui

  1. 保存ApplicationThread對象
  2. 經過ApplicationThread對象調用ActivityThread的bindApplication()方法,bindApplication()方法就是在應用進程側發送了一個BIND_APPLICATION消息而後就返回了,因此說bindApplication()方法是一個異步的方法。注意,此時應用進程中的looper並無開始循環,因此這條message也僅是被增長到了消息隊列中。
  3. 若是有待啓動的Activity,給ActivityThread發送 CREATE_ACTIVITY 的消息,這個也是異步的。此時對於ActivityThread來講,主線程消息隊列中有兩條待處理的消息:1.bindApplication 2.createActivity。特別注意,此時主線程的Looper並無開始循環,因此在後面開始循環的時候,第一條消息必定會延後第二條消息的執行。另外,這裏只會發送一條 CREATE_ACTIVITY 的消息,由於只有最頂部的第一個待開啓的Activity會發送消息。
  4. 若是有待啓動的Service,給ActivityThread發送 CREATE_SERVICE 的消息,這個也是異步的。此時對於ActivityThread來講,主線程消息隊列中有大於等於3條待處理的消息:1.bindApplication 2.createActivity 3.createService(一條或多條,不像Activity,Service會爲全部等待啓動的Service發送消息到消息隊列)。注意,此時主線程的Looper一樣沒有開始循環,因此在後面開始循環的時候,前面幾條消息的執行,會延後這些Service的啓動。這樣問題就來了,service executing timeout類型的ANR,其在AMS中的超時倒計時消息是在發送 CREATE_SERVICE 消息的同時發送到AMS的消息隊列中的,即倒計時已經開始了,因此ActivityThread中排在Service啓動前的消息,其處理時間都會被計算在Service的啓動時間中,因此Service啓動的anr,並不必定是本身的啓動過程發生了耗時操做,也有多是application初始化有耗時或先啓動的Activity耗時形成的。
  5. 若是有待啓動的BroadcastReceiver,給ActivityThread發送 RECEIVER 的消息。邏輯與前面同樣,插到消息隊列等待執行。

特別特別注意一點,就是attach()方法執行完畢以前,主線程的Looper一直是沒有開始循環的狀態。attach()執行完畢後,纔會調用Looper.loop()開始消息循環。spa

當Looper開始循環以後,就會立刻開始處理消息隊列中的message。對於啓動Service來講,那麼此時隊列中的message包括兩條:1.BIND_APPLICATION 2.CREATE_SERVICE。

下面咱們看下這兩條消息都作了什麼,BIND_APPLICATION。
BIND_APPLICATION 在ActivityThread裏對應的就是handleBindApplication()方法。

private void handleBindApplication(AppBindData data) {
    ...
    Application app = data.info.makeApplication(...);
    ...
}
複製代碼

從上面咱們看到有一個咱們常常接觸的操做——Application的初始化,讓咱們繼續看看makeApplication作了什麼? (data.info 是一個LoadedApk對象)

public Application makeApplication(...){
    ...
    app = mActivityThread.mInstrumentation.newApplication(...);
    ...
    instrumentation.callApplicationOnCreate(app);
    ...
}
複製代碼

上面就是經過反射建立Application對象,而後調用application.onCreate()方法。這說明啓動Service的過程當中涉及到application的初始化。

接下來,咱們看看第二條message作了什麼?CREATE_SERVICE 在ActivityThread裏對應的就是handleCreateService()方法

private void handleCreateService(...) {
    ...
    service = (Service) cl.loadClass(data.info.name).newInstance();
    ...
    service.onCreate();
    ...
    ActivityManagerNative.getDefault().serviceDoneExecuting(...);
    ...
}
複製代碼

這個方法作了三件事,反射建立Service對象,調用Service的onCreate(),通知AMS服務啓動完成。

上面就是Service啓動流程的大體介紹,有了對流程的簡單瞭解,還不足以分析產生ANR的緣由。咱們還要再瞭解一下ANR的觸發機制。

2.3 ANR觸發機制

這個機制的本質就是AMS在某個時間節點發送一個「超時等待」的message到本身的消息隊列中,若是超時時間結束前,service啓動了,就移除這個message,若是沒啓動,這個message就會被執行。這個message執行的內容最終就是調用AMS的appNotResponding()方法觸發ANR操做。若是你們有意願更深刻的瞭解ANR的觸發原理,能夠閱讀一下GitYuan的博文理解Android ANR的觸發原理

那麼對於Service來講,是在什麼節點發送的「超時等待」的message,又是在何時移除的message呢?

回顧一下2.1節中的圖2,以及2.2.2.1節中對attach()方法分析的第4個步驟,AMS是在進程attach,發送建立Service消息的時候,一併將「超時等待」消息發出去的。具體的方法能夠查看該步驟調用的方法(ActiveServices.realStartServiceLocked()->bumpServiceExecutingLocked())。而後在ActivityThread.handleCreateService()方法中,經過ActivityManagerNative.getDefault().serviceDoneExecuting(...)方法告訴AMS服務啓動完成,此時AMS會移除「超時等待」消息。

2.4 可能形成ANR的緣由

在完成對service啓動流程和ANR觸發原理的簡單介紹以後,咱們來最終分析一下可能形成ANR的緣由。

從上面的分析咱們能夠看到,新的進程在Looper開始循環前,消息隊列中有兩個消息等待處理(BIND_APPLICATION,CREATE_SERVICE),而且此時AMS側已經開始對Service啓動過程進行倒計時。因此對於Service來講,其啓動時間會受到兩個方法的影響:1.Application.onCreate() 2.Service.onCreate()。也就是說兩個方法中任何一個方法有耗時操做,都有可能形成ANR。

3. 結尾

本文只是一個對Service啓動流程與ANR的簡單介紹,主要是爲了解決本身一直以來的一個困惑,即到底Application.onCreate()會不會形成Service的ANR,也但願能給同樣困惑的人解釋清楚。若是但願深刻了解,建議仍是經過閱讀源代碼仔細學習一下Service的啓動流程以及ANR的觸發原理,這兩個點理解以後,對啓動Service形成的ANR就會更加明晰。

流程圖見 github.com/kishimotoin…

相關文章
相關標籤/搜索