Android 應(yīng)用點(diǎn)擊圖標(biāo)到Activity界面顯示的過(guò)程分析

分析這個(gè)過(guò)程不是單純?yōu)樽咭槐楹瘮?shù)調(diào)用的流程,而是更好的理解平常用到的一些方法旭咽、

對(duì)象的初始化時(shí)間贞奋,對(duì)象創(chuàng)建的個(gè)數(shù),方法的先后順序穷绵,以及每個(gè)類轿塔,

方法背后的作用和目的。主要是一下幾個(gè)問(wèn)題:

  1. Application是什么時(shí)候創(chuàng)建的,每個(gè)應(yīng)用程序有幾個(gè)Application
  2. 應(yīng)用的資源路徑什么時(shí)候初始化的
  3. 應(yīng)用中ContextImpl的個(gè)數(shù)
  4. Application.attach(),Activity.attach()的調(diào)用時(shí)機(jī)及作用
  5. Instrumentation的重要性及具體作用
  6. 點(diǎn)擊Launcher啟動(dòng)Activity和應(yīng)用內(nèi)部啟動(dòng)Activity的區(qū)別
1. 應(yīng)用的安裝過(guò)程

應(yīng)用安裝的時(shí)候,通過(guò)PMS解析apk的AndroidManifest.xml文件,

提取出這個(gè)apk的信息寫(xiě)入到packages.xml文件中勾缭,

這些信息包括:權(quán)限揍障、應(yīng)用包名、icon俩由、APK的安裝位置毒嫡、版本、userID等等采驻。

packages.xml文件位于系統(tǒng)目錄下/data/system/packages.xml。

2. 系統(tǒng)啟動(dòng)開(kāi)啟的服務(wù)

系統(tǒng)的會(huì)在啟動(dòng)時(shí)也可以認(rèn)為開(kāi)機(jī)時(shí)啟動(dòng)常用的服務(wù),

如ActivityManagerService(AMS),

PackageManagerService(PMS),

WindowManagerService(WMS),

以及ServiceManager(SM),

用于管理各種服務(wù),詳細(xì)的管理方式見(jiàn)理解Binder框架匈勋。

同時(shí)桌面Launcher會(huì)為安裝過(guò)的應(yīng)用生成不同的應(yīng)用入口,對(duì)應(yīng)桌面上的應(yīng)用圖標(biāo)礼旅,

下面分析點(diǎn)擊應(yīng)用圖標(biāo)的到應(yīng)用啟動(dòng)的過(guò)程。

這里主要是應(yīng)用端的過(guò)程,服務(wù)端也就是AMS少量涉及,

同時(shí)以大體框架為主,不深入代碼細(xì)節(jié)洽洁。主要分為L(zhǎng)auncher進(jìn)程痘系,AMS進(jìn)程,應(yīng)用程序進(jìn)程饿自。

  • Instrumentation: 用于管理應(yīng)用程序和系統(tǒng)(主要與應(yīng)用程序內(nèi)的Activity)的交互過(guò)程汰翠,Instrumentation將在任何應(yīng)用程序運(yùn)行前初始化,每個(gè)進(jìn)程只會(huì)存在一個(gè)Instrumentation對(duì)象昭雌,且每個(gè)Activity都有此對(duì)象的引用复唤,可以通過(guò)它監(jiān)測(cè)系統(tǒng)與應(yīng)用程序之間的所有交互,主要是內(nèi)部交互烛卧。
  • **ActivityThread: **App的真正入口,通過(guò)調(diào)用main()App開(kāi)始真正運(yùn)行佛纫,同時(shí)開(kāi)啟消息循環(huán)隊(duì)列,雖然不是一個(gè)真正的線程,但一般所在的線程被稱為UI線程或主線程总放。ActivityThread就是專門(mén)與AMS的進(jìn)行外部交互呈宇。
  • ApplicationThread: 應(yīng)用需要和遠(yuǎn)程服務(wù)AMS等通信,而B(niǎo)inder只能單項(xiàng)通信,而AMS等服務(wù)想控制應(yīng)用需要應(yīng)用程序提供一個(gè)Binder接口,ApplicationThread就是這個(gè)Binder接口,用于通過(guò)遠(yuǎn)程服務(wù)調(diào)用本地的方法。
  • ActivityManagerProxy: AMS遠(yuǎn)程服務(wù)在本地的代理局雄。
  • ApplicationThreadProxy: ApplicationThread在遠(yuǎn)程服務(wù)AMS的代理甥啄。

其實(shí)Launcher本身也是一個(gè)Activity,我們不考慮Launcher的創(chuàng)建過(guò)程炬搭,只分析用戶的應(yīng)用程序的從點(diǎn)擊到Activity啟動(dòng)的過(guò)程蜈漓。上面的這些每個(gè)進(jìn)程只存在一個(gè),如果應(yīng)用存在多個(gè)進(jìn)程宫盔,就會(huì)有多個(gè)實(shí)例迎变。

Launcher所在進(jìn)程,Launcher通過(guò)Binder與ActivityManagerService與通信飘言。

  Launcher.startActivitySafely
  |
  Launcher.startActivity  
  |
  Activity.startActivity
  |   
  Activity.startActivityForResult
  | 
  Instrumentation.execStartActivity
  |
  ActivityManagerNative.getDefault().startActivity
  |  
  ActivityManagerProxy.startActivity

上面的這些都是在一條調(diào)用鏈上衣形,ActivityManagerProxy是AMS的本地代理,實(shí)際的工作是在遠(yuǎn)程AMS完成的,下面是AMS進(jìn)程谆吴。

  借助binder驅(qū)動(dòng)
  ActivityManagerService.startActivity-> (AMS)  
  ...
  //一系類AMS的調(diào)用鏈和一些與Launcher通過(guò)Binder的互相調(diào)用過(guò)程倒源,此時(shí)仍然未創(chuàng)建應(yīng)用程序的進(jìn)程。
  ...
  * AMS創(chuàng)建一個(gè)新的進(jìn)程句狼,用來(lái)啟動(dòng)一個(gè)ActivityThread實(shí)例笋熬, 
  * 即將要啟動(dòng)的Activity就是在這個(gè)ActivityThread實(shí)例中運(yùn)行 
  Process.start("android.app.ActivityThread",...)->    
  // 通過(guò)zygote機(jī)制創(chuàng)建一個(gè)新的進(jìn)程    
  Process.startViaZygote->調(diào)用新進(jìn)程的main()
  ActivityThread.main->

創(chuàng)建新進(jìn)程的時(shí)候,AMS會(huì)保存一個(gè)ProcessRecord信息腻菇,Activity應(yīng)用程序中的AndroidManifest.xml配置文件中胳螟,我們沒(méi)有指定Application標(biāo)簽的process屬性,系統(tǒng)就會(huì)默認(rèn)使用package的名稱筹吐。每一個(gè)應(yīng)用程序都有自己的uid糖耸,因此,這里uid + process的組合就可以為每一個(gè)應(yīng)用程序創(chuàng)建一個(gè)ProcessRecord丘薛。每次在新建新進(jìn)程前的時(shí)候會(huì)先判斷這個(gè)ProcessRecord是否已存在嘉竟,如果已經(jīng)存在就不會(huì)新建進(jìn)程了,這就屬于應(yīng)用內(nèi)打開(kāi)Activity的過(guò)程了。

AMS通過(guò)開(kāi)啟新的進(jìn)程并調(diào)用ActivityThread.main后洋侨,接下來(lái)就是應(yīng)用程序進(jìn)程了舍扰。

public static void main(String[] args)  {
    Looper.prepareMainLooper();
    //又新建一個(gè)ActivityThread并調(diào)用attach(false)
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {    
        sMainThreadHandler = thread.getHandler();
    }
}

thread.attach(false)主要是為了將ApplicationThread通過(guò)Binder驅(qū)動(dòng)"傳遞"到遠(yuǎn)程AMS,也就是綁定,主要
是為了讓AMS能通過(guò)ApplicationThread的代理ApplicationThreadProxy來(lái)調(diào)用ApplicationThread的方法希坚,而本地應(yīng)用程序通過(guò)ActivityManagerProxy來(lái)調(diào)用遠(yuǎn)程ActivityManagerService的方法边苹,相當(dāng)于應(yīng)用程序與
AMS的通信窗口。

注意此時(shí)只創(chuàng)建了應(yīng)用程序的ActivityThread和ApplicationThread,和開(kāi)啟了Handler消息循環(huán)機(jī)制裁僧,其他的都還未創(chuàng)建, ActivityThread.attach(false)又會(huì)最終到AMS的attachApplication勾给,這個(gè)工程其實(shí)是將本地的ApplicationThread傳遞到AMS。然后AMS就可以通過(guò)ApplicationThread的代理ApplicationThreadProxy來(lái)調(diào)用應(yīng)用程序ApplicationThread.bindApplication锅知,通知應(yīng)用程序的ApplicationThread已和AMS綁定播急,可以不借助其他進(jìn)程幫助直接通信了。此時(shí)Launcher的任務(wù)也算是完成了售睹。過(guò)程如下:

應(yīng)用進(jìn)程:
ActivityThread.attach
|    
IActivityManager.attachApplication(mAppThread)
| 
ActivityManagerProxy.attachApplication(mAppThread)   

AMS進(jìn)程:
ActivityManagerService.attachApplication
|
ApplicationThreadProxy.bindApplication 

應(yīng)用進(jìn)程:

ApplicationThread.bindApplication
| Handler通信
AplicationThread.handlerBindApplication

ApplicationThreadProxy.bindApplication(...)會(huì)傳來(lái)這個(gè)應(yīng)用的一些信息桩警,如ApplicationInfo,Configuration等,在ApplicationThread.bindApplication里會(huì)待信息封裝成'AppBindData',通過(guò)

sendMessage(H.BIND_APPLICATION, data)

將信息放到應(yīng)用里的消息隊(duì)列里昌妹,通過(guò)Handler消息機(jī)制捶枢,在ActivityThread.handleMeaasge里處理H.BIND_APPLICATION的信息,調(diào)用AplicationThread.handleBindApplication飞崖。

handleBindApplication(AppBindData data) {
    Process.setArgV0(data.processName);//設(shè)置進(jìn)程名
    ...
    //初始化mInstrumentation
    if(data.mInstrumentation!=null) {
        mInstrumentation = (Instrumentation)    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
    }else {
        mInstrumentation = new Instrumentation();
    }

    //創(chuàng)建Application烂叔,data.info是個(gè)LoadedApk對(duì)象。
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;

    //調(diào)用Application的onCreate()方法固歪。
    mInstrumentation.callApplicationOnCreate(app);
}

LoadedApk類:
public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {

    if (mApplication != null) {   
       return mApplication;
    }

    String appClass = mApplicationInfo.className;
    java.lang.ClassLoader cl = getClassLoader();

    //此時(shí)新建一個(gè)Application的ContextImpl對(duì)象蒜鸡,
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

    //通過(guò)在handleBindApplication創(chuàng)建的mInstrumentation對(duì)象新建一個(gè)Application對(duì)象胯努,同時(shí)進(jìn)行attach。
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
    appContext.setOuterContext(app);
 }

Instrumentation類:
public Application newApplication(ClassLoader cl, String className, Context context) {    
    return newApplication(cl.loadClass(className), context);
}

Instrumentation類:
static public Application newApplication(Class<?> clazz, Context context)  {
    //實(shí)例化Application
    Application app = (Application)clazz.newInstance();     

    // Application和context綁定
    app.attach(context);    
    return app;
}

//attach就是將新建的ContextImpl賦值到mBase,這個(gè)ContextImpl對(duì)象就是所有Application內(nèi)Context的具體
//實(shí)現(xiàn)逢防,同時(shí)賦值一些其他的信息如mLoadedApk叶沛。
final void attach(Context context) {    
    mBase = base;  
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

這時(shí)Application就創(chuàng)建好了,這點(diǎn)很重要忘朝,很多博客里說(shuō)Application是在performLaunchActivity里創(chuàng)建的灰署,因?yàn)閜erformLaunchActivity也有mInstrumentation.newApplication這個(gè)調(diào)用,newApplication函數(shù)中可看出會(huì)先判斷是否以及創(chuàng)建了Application,如果之前已經(jīng)創(chuàng)建局嘁,就返回已創(chuàng)建的Application對(duì)象溉箕。

ApplicationThreadProxy.bindApplication完成后,同時(shí)在AMS進(jìn)程悦昵,調(diào)用realStartActivityLocked肴茄,接著就通過(guò)ApplicationThreadProxy調(diào)用到應(yīng)用程序進(jìn)程

//AMS進(jìn)程:
ActivityManagerService.realStartActivityLocked
|
ApplicationThreadProxy.scheduleLaunchActivity

//應(yīng)用程序進(jìn)程
ApplicationThread.scheduleLaunchActivity
|  Handler通信
sendMessage(H.LAUNCH_ACTIVITY, r);
|  handleMassage
ActivityThread.handleLaunchActivity
|
ActivityThread.performLaunchActivity {
    //類似Application的創(chuàng)建過(guò)程,通過(guò)classLoader加載到activity.
    activity = mInstrumentation.newActivity(classLoader, 
               component.getClassName(), r.intent);
    //因?yàn)锳ctivity有界面旱捧,所以其Context是ContextThemeWrapper類型独郎,但實(shí)現(xiàn)類仍是ContextImpl.
    Context appContext = createBaseContextForActivity(r, activity);

    activity.attach(context,mInstrumentation,application,...);

    //attach后調(diào)用activity的activity方法踩麦。
    mInstrumentation.callActivityOnCreate(activity,...)

}

在ActivityThread.handleLaunchActivity里枚赡,接著調(diào)用
|
ActivityThread.performResumeActivity
|
activity.performResume
|
mInstrumentation.callActivityOnResume(this);
|
this.onResume;

activity.onResume就是和Window,View之類的綁定相關(guān)了谓谦,此時(shí)界面就顯示出來(lái)了贫橙。

3. 總結(jié)
  1. Application是在ActivityThread.handleBindApplication中創(chuàng)建的,一個(gè)線程只會(huì)創(chuàng)建一個(gè)Application反粥,但一個(gè)應(yīng)用程序如果有多個(gè)進(jìn)程將會(huì)創(chuàng)建多個(gè)Application對(duì)象卢肃。

  2. 應(yīng)用資源是在Application初始化的時(shí)候,也就是創(chuàng)建Application ContextImpl的時(shí)候,ContextImpl就包含這個(gè)路徑才顿,主要就是對(duì)就是ResourcesManager這個(gè)單例的引用莫湘。

  3. 可以看出每次創(chuàng)建Application和Acitvity以及Service時(shí)就會(huì)有一個(gè)ContextImpl實(shí)例,ContentProvider和BroadcastReceiver的Context是其他地方傳入的郑气。所以Context數(shù)量=Application數(shù)量+Activity數(shù)量+Service數(shù)量幅垮,單進(jìn)程情況下Application數(shù)量就是1。

  4. attach是依附尾组、貼上的意思忙芒,可以理解為將兩種事物聯(lián)系在一起,上面分析的過(guò)程主要涉及3個(gè)attach讳侨,ActivityThread.attach可以理解為將應(yīng)用程序進(jìn)程的ApplicationThread依附到AMS呵萨,和AMS聯(lián)系起來(lái),用于整個(gè)AMS和應(yīng)用程序的通信跨跨,Application.attach和Activity.attach主要是將新創(chuàng)建的ContextImpl對(duì)象與Application潮峦,Activity,Service等組件聯(lián)系起來(lái),對(duì)組件中其中的Context mBase變量賦值跑杭,以及一些初始化工作铆帽,比如MainHandler的賦值,mWindow的初始化(如果存在)等德谅。

  5. ContextImpl包含資源信息爹橱、對(duì)Context的一些函數(shù)的實(shí)現(xiàn)等。 可以很好的理解窄做,attach需要在Application愧驱,Activity等組件調(diào)用onCreat之前調(diào)用,因?yàn)樾枰韧瓿山M件的初始化工作椭盏。

  6. 管理著組件Application,Activity组砚,Service等的創(chuàng)建,生命周期調(diào)用掏颊,很重要的一個(gè)類糟红。例如:
    //創(chuàng)建Application
    mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
    //調(diào)用Application的onCreate()方法。
    mInstrumentation.callApplicationOnCreate(app);

    //創(chuàng)建Activity,實(shí)際生命周期的管理乌叶。
    mInstrumentation.newActivity(classLoader, component.getClassName(), r.intent);
    mInstrumentation.callActivityOnCreate(activity);
    mInstrumentation.callActivityOnOnResume(activity);
    ...
    
  7. 點(diǎn)擊Launcher時(shí)會(huì)創(chuàng)建一個(gè)新進(jìn)程來(lái)開(kāi)啟Activity盆偿,而應(yīng)用內(nèi)打開(kāi)Activity,如果Activity不指定新進(jìn)程,將在原來(lái)進(jìn)程打開(kāi)准浴,是否開(kāi)啟新進(jìn)程實(shí)在AMS進(jìn)行控制的事扭,上面分析得到,每次開(kāi)啟新進(jìn)程時(shí)會(huì)保存進(jìn)程信息乐横,默認(rèn)為應(yīng)用包名+應(yīng)用UID,打開(kāi)Activity時(shí)會(huì)檢查請(qǐng)求方的信息來(lái)判斷是否需要新開(kāi)進(jìn)程求橄。Launcher打開(kāi)Activity默認(rèn)ACTIVITY_NEW_TASK,新開(kāi)一個(gè)Activity棧來(lái)保存Activity的信息葡公。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末罐农,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子催什,更是在濱河造成了極大的恐慌涵亏,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛆楞,死亡現(xiàn)場(chǎng)離奇詭異溯乒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)豹爹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)裆悄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人臂聋,你說(shuō)我怎么就攤上這事光稼』蚰希” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我稻扬,道長(zhǎng)讲逛,這世上最難降的妖魔是什么渺尘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逝薪。我一直安慰自己,他們只是感情好蝴罪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布董济。 她就那樣靜靜地躺著,像睡著了一般要门。 火紅的嫁衣襯著肌膚如雪虏肾。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天欢搜,我揣著相機(jī)與錄音封豪,去河邊找鬼。 笑死狂巢,一個(gè)胖子當(dāng)著我的面吹牛撑毛,可吹牛的內(nèi)容都是我干的书聚。 我是一名探鬼主播唧领,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雌续!你這毒婦竟也來(lái)了斩个?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驯杜,失蹤者是張志新(化名)和其女友劉穎受啥,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸽心,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滚局,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顽频。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藤肢。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糯景,靈堂內(nèi)的尸體忽然破棺而出嘁圈,到底是詐尸還是另有隱情省骂,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布最住,位于F島的核電站钞澳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涨缚。R本人自食惡果不足惜轧粟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脓魏。 院中可真熱鬧逃延,春花似錦、人聲如沸轧拄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)檩电。三九已至拄丰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俐末,已是汗流浹背料按。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卓箫,地道東北人载矿。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像烹卒,于是被迫代替她去往敵國(guó)和親闷盔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容