天天看點

Android M應用啟動流程分析

更新:

2016-10-29:更新handlebindapplication部分。

我這個版本的原則是,有話則長,無話則短.

以下分析基于6.0.1_r10版本。

先看一張大圖:

Android M應用啟動流程分析

我們暫時忽略input處理的過程,以桌面的onclick事件被觸發為起點。

這部分根據launcher的不同而大同小異。

路徑:frameworks/base/services/core/java/com/android/server/am/activitymanagerservice.java

桌面調用framework,最後會調到ams的startactivity方法.

現在是多使用者時代了,startactivity現在唯一做的事兒,就是通過userhandle.getcallinguserid()去擷取目前的user id,然後調用startactivityasuser方法。

這個方法如其名,還真是隻處理跟user相關的工作,調用handleincominguser。

之後,調用activitystacksupervisor來去處理跟activity狀态相關真正邏輯。

路徑:frameworks/base/services/core/java/com/android/server/am/activitystacksupervisor.java

先解釋一下為什麼叫maywait,因為調用startactivity是可能要等待結果的startactivityforresult,那就要挂起調用者。

首先,startactivitymaywait要讀取一些資訊。從4.4開始,這部分邏輯寫到resolveactivity方法中,它會調用packagemanagerservice的resolveintent方法。這個方法會先調用queryintentactivities方法出查詢相關的清單,然後再調用choosebestactivity方法去選擇。為了不影響主線,這些支線内容後面再講。

主線往下走,進入startactivitylocked。locked意思是調用者需要保證加鎖保護,不能重複調用,在startactivitymaywait中,是采用mservice對象,也就是構造activitystacksupervisor時傳進來的activitymanagerservice的對象。

調用成功了之後,如果需要wait,就讓mservice.wait()去等待吧。新activity還在征途上。

前面先做一系列檢查的工作,比如權限,比如intent防火牆檢查。

準備做好之後,就new一個activityrecord,用于存儲activity的各種狀态和曆史資訊。

然後,通過getfocusedstack方法擷取目前擷取焦點的activitystack。activitystacksupervisor中的mfocusedstack中儲存了目前的前台activitystack。

下面就準備切換新程序了,先判斷一下是否可以切換,如果處于通話中界面等無法馬上切換的情況。通過activitymanagerservice的checkappswitchallowedlocked方法來做檢查,如果目前不允許做程序切換,就先存到pendingactivitylaunch的清單中,等待以後有機會再調用。

如果允許做切換,那麼先檢查一下目前是否有以前的等待任務,如果有就先執行它們,調用dopendingactivitylauncheslocked方法去執行這個循環。

如果以上都完成了,就調用startactivityuncheckedlocked。

通過一系列分析,找到了該執行的目标activitystack,然後調用該activitystack的startactivitylocked方法針對該任務做具體的操作。

路徑:frameworks/base/services/core/java/com/android/server/am/activitystack.java

如果需要的話,這一步中會調用到wms的setappstartingwindow,開始準備新應用的啟動視窗。

這其中,有重要一步是調用windowmanagerservice的addapptoken方法去将資訊共步給wms,為下一步的顯示做準備。

最後,調用activitystacksupervisor的resumetopactivitieslocked方法,将顯示的activities都resume一下。

先擷取目前焦點顯示的activitystack,調其resumetopactivitylocked。完成後,周遊所有能顯示的activity的stack。

調用resumetopactivityinnerlocked

這是第一次進入這個方法,這次我們是走pause桌面這一支,下一次我們就走到最後的startspecificactivitylocked那一支。

調用startpausinglocked去pause。

ams開始發起pause桌面的操作

如果不是從桌面啟動的話,就要去為上一個應用抓個用于顯示在近期任務裡的圖。

prev.updatethumbnaillocked(screenshotactivities(prev), null);

處理完成之後,通知桌面應用去執行onpause。

prev.app.thread.schedulepauseactivity(prev.apptoken, prev.finishing,userleaving, prev.configchangeflags, dontwait);

這個thread就是ipc的applicationthreadnative對象。

路徑:frameworks/base/core/java/android/app/applicationthreadnative.java

ams要通過ipc來通知給桌面,于是通過proxy來發送ipc操作.

路徑:frameworks/base/core/java/android/app/activitythread.java

通過ipc,運作桌面應用的activitythread的schedulepauseactivity。此處activitythread會将這個請求放入隊列中,等待運作。

在這段期間,wms也沒閑着,類似于之前我們在startactivitylocked時做的addwindow之類的操作一直在幹活。

終于從隊列中輪到出場了,開始執行桌面的onpause吧。

不過先别急,執行onpause之前,先執行performuserleavingactivity,最後會調到activity的performuserleaving。

這個方法做兩步:

這些都做完了,調用performpauseactivity。

首先判斷一下狀态,如果已經pause了,那就需要先resume之。當然,如果pause了之後正在finishing中,就算了,不是的話,抛個runtimeexception,問問調用者不先resume是為哪般。

沒有異常的話,先調用callcallactivityonsaveinstancestate,這個會通過instrumentation的callactivityonsaveinstancestate去調用activity的performsaveinstancestate, 然後會調到activity的onsaveinstancestate。還會将對話框的資訊做儲存操作。

儲存完狀态之後,再調用instrumentation的callactivityonpause。然後調用activity的performpause。

activity在onpause之前,先通知各個fragment去onpause,再調用activity的onpause.

performpauseactivity結束後,回到launchpauseactivity,下面通知ams,調ipc來做activitypaused。

路徑:frameworks/base/core/java/android/app/activitymanagernative.java

桌面的onpause執行完了,通過ipc通知ams,可以啟動新應用了。

路徑:services/core/java/com/android/server/am/activitymanagerservice.java

ams收到activitypaused的消息,然後找到對應的activitystack的activitypausedlocked。

然後調用completepausedlocked。

到此,桌面的onpause正式告一段落。

結束之後,再次調用前面我們已經遇到過的activitystacksupervisor的resumetopactivitieslocked,前一次我們走了一半就調pause過程去了,這次我們将走到最後。

還跟上次一樣,調相應的activitystack的resumetopactivitylocked。

這個方法隻是一個十幾行的wrapper,除了設了個flag和處理鎖屏之外,直接調用resumetopactivityinnerlocked方法。

這個大方法走到最後,執行activitystacksupervisor的startspecificactivitylocked。

(注:這個方法是5.0之後分出來的,4.4上還在resumetopactivitylocked裡面)

從這裡,又從activitystacksupervisor調回activitymanagerservice,調用startprocesslocked。

通過調用android.os.process的start去啟動新程序。

路徑:frameworks/base/ core/java/android/os/process.java

其實就是startviazygote的一個簡單封裝。

路徑:frameworks/base/core/java/android/os/process.java

主要是處理參數,然後調用zygotesendargsandgetresult去通過socket通信去通知zygote。

通過socket通知zygote程序去fork新程序。接收方是zygoteconnection。

路徑:frameworks/base/core/java/com/android/internal/os/zygoteconnection.java

從socket中讀取指令并執行。

這個可以往下再分為4步:

readargumentlist

apply security policies

preforkandspecialize

路徑:/frameworks/base/core/java/com/android/internal/os/zygote.java

這步分為三個子步驟:

prefork

nativeforkandspecialize

postforkcommon

路徑:/libcore/dalvik/src/main/java/dalvik/system/ zygotehooks.java

這步離開了frameworks/base,進入了libcore。這裡面要注意,不能調用android的api,打個log什麼的都要注意。

daemons.stop()

停掉gc,停掉finalizer等,fork程序時不需要這些

waituntilallthreadsstopped()

確定fork之前隻有一個線程在運作

nativeprefork()

給虛拟機一個機會去在fork之前做點處理

路徑:/frameworks/base/core/jni/com_android_internal_os_zygote.cpp

這是我們第一次進入c++層。真正做事的函數是forkandspecializecommon。

真正開始fork新程序

setsigchldhandler

fork and detach(新程序從這一步開始誕生)

child process setup

這一步是值得大書特書的一步,因為從這一步開始,更具體地說是從fork and detach開始,新應用的程序終于fork出來了。從此zygote老程序的事情我們不再關心,我們來看新程序号就好了。

child process setup之後,有一個重要的函數會被執行到,這就是zygotehooks_nativepostforkchild。

在這個函數中,我們熟悉的aoc版本号的log會輸出出來。

這個函數的aoc版本路徑在:/aliyunos/aoc/vm/native/dalvik_system_zygotehooks.cc中。

對應的art版本在:/art/runtime/native/dalvik_system_zygotehooks.cc中。

路徑:/libcore/dalvik/src/main/java/dalvik/system/zygotehooks.java

隻幹一件事,把prefork裡面stop的monitor們重新打開。這一步要注意,原來的zygote裡面的不要去管了,隻看新程序的就好。

路徑:frameworks/base/core/java/com/android/internal/os/runtimeinit.java

初始化完成,通過反射來調用activitythread的main方法

路徑:frameworks/base/ core/java/android/app/activitythread.java

執行activitythread的main方法,新的應用正式上路

路徑:frameworks/base/

新的activity建好了,要通知ams,走ipc。

路徑: frameworks/base/services/core/java/com/android/server/am/activitymanagerservice.java

ams收到attach的通知,一切準備就緒。

路徑:frameworks/base/ services/core/java/com/android/server/am/activitymanagerservice.java

其它部分都是線性的,這部分我們不得不分成兩個部分各表一支了。這兩部分分别走ipc,最後在activitythread的隊列中彙合。

首先是bindapplication。

ipc調用

主要包括兩部分的操作,雖然這個方法不叫做schedulexxx,但是實際上兩步的操作都是放到隊列中。

到這一步的時候,其實我們隻是啟動了一個空程序而己,跟實際的apk還一點關系也沒有。

首先,如果services不為空的話,先初始化一下services cache。

servicemanager.initservicecache(services);

然後,schedule第一個任務,setcoresettings(coresettings);

這個最終會走到handlesetcoresettings。

下面,pm才出場去讀真正的package的資訊。讀好之後,再去将bind_application消息放到隊列裡去,這時候可能正在執行setcoresettings。

真正啟動activity之前,還得做一些準備工作。比如install provider就是在這時候做的。

我們都知道,在activity之外,對于每個應用,還對應一個application類。這個application就是在loadapk的makeapplication方法時構造的。

下面調用classloader,并且生成applicationcontext.

下面将通過intrumentation的newapplication方法去真正建立application

通過反射構造對象,然後調用application的attach方法。

attach再調用attachbasecontext。

50 @override

51 protected void attachbasecontext(context newbase) {

52 super.attachbasecontext(newbase);

53 }

65 protected void attachbasecontext(context base) {

66 if (mbase != null) {

67 throw new illegalstateexception("base context already set");

68 }

69 mbase = base;

70 }

586 if (instrumentation != null) {

587 try {

588 instrumentation.callapplicationoncreate(app);

589 } catch (exception e) {

590 if (!instrumentation.onexception(app, e)) {

591 throw new runtimeexception(

592 "unable to create application " + app.getclass().getname()

593 + ": " + e.tostring(), e);

594 }

595 }

596 }

598 // rewrite the r 'constants' for all library apks.

599 sparsearray packageidentifiers = getassets(mactivitythread)

600 .getassignedpackageidentifiers();

601 final int n = packageidentifiers.size();

602 for (int i = 0; i < n; i++) {

603 final int id = packageidentifiers.keyat(i);

604 if (id == 0x01 || id == 0x7f) {

605 continue;

606 }

607

608 rewritervalues(getclassloader(), packageidentifiers.valueat(i), id);

609 }

610

611 return app;

612 }

mwindowsession = windowmanagerglobal.getwindowsession();

if (mwindowmap.containskey(client.asbinder())) {

}

win = new windowstate(this, session, client, token,attachedwindow, appop0, seq, attrs, viewvisibility, displaycontent);

getrunqueue().executeactions(mattachinfo.mhandler);

windowsizemaychange |= measurehierarchy(host, lp, res,desiredwindowwidth, desiredwindowheight);

winanimator.applyenteranimationlocked();

surfacecontrol surfacecontrol = winanimator.createsurfacelocked();

void drawframetask::postandwait() {

automutex _lock(mlock);

mrenderthread->queue(this);

msignal.wait(mlock);

繼續閱讀