本文針對Service啓動過程形成的ANR進行分析,同時啓動過程限制在如下兩個條件中:
(A進程,調用startService()方法的進程;B進程,須要開啓的Service所在的進程,A和B不是同一個進程)android
爲了分析ANR所產生的緣由,對於在不一樣進程中啓動Service的流程須要有一個簡單的瞭解,下面首先簡要分析一個這個過程。git
首先來看下簡化的啓動流程時序圖,共分爲兩張圖。第一張圖是描述startService()的調用過程,第二張圖是描述startProcess的調用過程。github
圖1:bash
圖2:app
從圖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
進程在啓動起來以後,會調用ActvityThread的main方法。這個方法主要作了兩件事:學習
attach()方法在ActivityThread一側,最主要的是調用了AMS的attachApplication()方法,將自身進程的ApplicationThread對象發送給AMS。而後在AMS一側,AMS則作了如下操做:ui
特別特別注意一點,就是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的觸發機制。
這個機制的本質就是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會移除「超時等待」消息。
在完成對service啓動流程和ANR觸發原理的簡單介紹以後,咱們來最終分析一下可能形成ANR的緣由。
從上面的分析咱們能夠看到,新的進程在Looper開始循環前,消息隊列中有兩個消息等待處理(BIND_APPLICATION,CREATE_SERVICE),而且此時AMS側已經開始對Service啓動過程進行倒計時。因此對於Service來講,其啓動時間會受到兩個方法的影響:1.Application.onCreate() 2.Service.onCreate()。也就是說兩個方法中任何一個方法有耗時操做,都有可能形成ANR。
本文只是一個對Service啓動流程與ANR的簡單介紹,主要是爲了解決本身一直以來的一個困惑,即到底Application.onCreate()會不會形成Service的ANR,也但願能給同樣困惑的人解釋清楚。若是但願深刻了解,建議仍是經過閱讀源代碼仔細學習一下Service的啓動流程以及ANR的觸發原理,這兩個點理解以後,對啓動Service形成的ANR就會更加明晰。