Android系統流程

keyboard3發表於2018-01-26

Zygote的啟動過程

1.系統啟動init程式時會啟動Zygote程式(負責Android執行時程式和應用程式的啟動)
2.Zygote程式會首先啟動SystemServer程式,SystemServer程式會啟動系統的關鍵服務(PMS和AMS)
3.當啟動應用程式時,AMS通過socket向Zygote通知建立新的程式

Android應用程式安裝過程

  • 系統啟動安裝過程
  1. SystemServer程式啟動PackageManagerService服務例項,新增到ServiceManager(負責管理系統中的Binder物件)

  2. PackageManagerService.main()掃描指定目錄的apk歸檔檔案

/system/framework
/system/app
/vendor/app
/data/app
/data/app-private
複製程式碼
  1. 對apk檔案解析AndroidManifest.xml各個標籤,得到的package、provider、service、receiver和activity等資訊儲存在PackageManagerService服務的記憶體中。
  • 下載安裝過程 下載apk->拷貝到指定目錄->解析apk歸檔檔案的AndroidManifest.xml的應用程式的資訊到packageManagerServices中 #應用程式啟動流程
  1. Launcher通過Binder程式間通訊機制通知ActivityManagerService,它要啟動一個Activity;
  2. ActivityManagerService通過Binder程式間通訊機制通知Launcher進入Paused狀態;
  3. Launcher通過Binder程式間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程式,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;
  4. ActivityThread通過Binder程式間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;
  5. AMS通知ActivityThread物件中呼叫bindApplication()方法. 送到訊息佇列中, 最終通過handleBindApplication()方法處理該訊息. 然後呼叫makeApplication()方法來載入Application的classes到記憶體中,然後通知AMS。
  6. ActivityManagerService通過Binder程式間通訊機制通知ActivityThread,可以真正執行Activity的啟動。

Launcher啟動過程

  1. SystemServer程式啟動ActivityManagerService服務例項,新增到ServiceManager(負責管理系統中的Binder物件)
  2. ActivityManagerService向PackageManagerService查詢Category型別為HOME的Activity,通過 ActivityStack.startActivityLocked將Launcher啟動起來
  3. Launcher在onCreate()中,通過這個PackageManagerService.queryIntentActivities介面來取回所有Action型別為Intent.ACTION_MAIN,並且Category型別為Intent.CATEGORY_LAUNCHER的Activity。顯示這些到頁面上。

Activity啟動流程

  1. AMS建立新程式,建立ActivityThread例項,將ApplicationThread的Binder物件傳遞給AMS並且開始了訊息迴圈。
  2. 從新程式呼叫到ActivityManagerService程式中,獲取要在新程式啟動的服務的相關資訊;
  3. 從ActivityManagerService程式又回到新程式中,最終將服務啟動起來。
    3.1. performLaunchActivity()初始化Activity
     3.1.1 classLoader載入Activity類,mInstrumentation.newActivity例項化Activity
     3.1.2 makeApplication如果程式中沒有就重新建立
     3.1.3 Activity.attach()將上下文資訊設定到Activity中 包括window建立
     3.1.4 mInstrumentation.callActivityOnCreate->onCreate()呼叫setContentView為decorView設定頁面內容
     3.1.5 Instrumentation.callActivityOnStart->onStart() 初始化應用資料準備
    3.2 handleResumeActivity啟用Activity
     3.2.1 mInstrumentation.callActivityOnResume->onResume()做準備顯示到互動的處理
     3.2.2 wm.addView新增window的decorView開始繪製顯示頁面互動

Service啟動流程

service將計算型的邏輯單獨放在一個程式內執行提高使用者體驗。

start流程

  1. AMS建立新程式(會判斷ProcessRecord不存在才去建立),建立ActivityThread例項,將ApplicationThread的Binder物件傳遞給AMS並且開始了訊息迴圈。
  2. 從新程式呼叫到ActivityManagerService程式中,獲取要在新程式啟動的服務的相關資訊;
  3. 從ActivityManagerService程式又回到新程式中,最終將服務啟動起來。
    3.1 handleCreateService()初始化Service
     3.1.1 通過ClassLoader載入service類,然後的newInstance例項化Service
     3.1.2 makeApplication如果程式中沒有就重新建立
     3.1.3 service.attach() 上下文資訊設定到service中
     3.1.4 service.onCreate()回撥
  4. 然後還會回新程式ApplicationThread.scheduleServiceArgs在主執行緒中回撥onStartCommand

bind流程

  1. MainActivity呼叫bindService函式通知ActivityManagerService,它要啟動CounterService這個服務,ActivityManagerService於是在MainActivity所在的程式內部把CounterService啟動起來,並且呼叫它的onCreate函式;
  2. ActivityManagerService把CounterService啟動起來後,繼續呼叫CounterService的onBind函式,要求CounterService返回一個Binder物件給它;
  3. ActivityManagerService從CounterService處得到這個Binder物件後,就把它傳給MainActivity,即把這個Binder物件作為引數傳遞給MainActivity內部定義的ServiceConnection物件的onServiceConnected函式;
  4. MainActivity內部定義的ServiceConnection物件的onServiceConnected函式在得到這個Binder物件後,就通過它的getService成同函式獲得CounterService介面。

PhoneWindow流程

  1. 在Activity的attach中建立PhoneWindow並完成相關設定
    1.1 呼叫PolicyManager.makeNewWindow建立PhoneWindow,
    1.2 設定window的Calback將IO輸入事件分發給Activity(softMode設定)
    1.3 設定windowManager,mAppToken為ActivityManagerService側ActivityRecord(Binder物件),mAppName為Activity元件名稱
  2. 在onCreate中的setContentView(rId)完成window的檢視內容的設定
    2.1 installDecor()根據Theme和Feature完成window的頂級檢視mDecor以及找到mContentParent
    2.2 通過mLayoutInflater.inflate(layoutResID, mContentParent)設定到mContentParent中
  3. 在handleResumeActivity()中wm.addView()將window的內容繪製出來
    3.1 WindowManagerImpl.addView將decorView、viewRoot、LayoutParamas分別新增到mViews[i]、mRoots[i]和mParams[I]中
    3.2 viewRootImpl.setView()完成相關成員的配置工作和繪製
     3.2.1 將decorView設定到mAttachInfo.mRootView上
     3.2.2 panelParentView不為空表示window是子視窗,需要儲存ApplicationWindowToken到mAttachInfo中
     3.2.3 呼叫requestLayout()執行view樹的繪製流程

View繪製流程

  1. viewRootImpl的requestLayout()和invalidateChildInParent()最終都呼叫performTraversals()開始執行測量、佈局、繪製
  2. mView.measure測量所有view的寬高 這裡就比按照流程敘述了,按照我自己的通俗的理解來講
    前言:view測量時有限制MeasureSpec。自己的申請(layout_)告訴父親,父親根據自己的情況計算家裡的情況給出的錢(空間)的使用限制。Mode:家裡的情況,Size:到手錢 自己當爸爸了 viewGroup(詳情getChildMeasureSpec)
      家裡的情況 Mode,拿到手上有 size大小的錢
  1.不管家裡任何情況,只要兒子申請花調固定的數字childDimension>=0,超過限制也給,
兒子的Mode=EXACTLY,Size=childDimension
  2.其他的話看家裡情況
            都用掉 EXACTLY: 
                  兒子申請都給我吧 MATCH_PARENT:目標明確,給。兒子的Mode=EXACTLY,Size=size
                  兒子申請我自己算 WRAP_CONTENT:兒子的Model=AT_MOST,Size=size
        家裡手頭緊不能超過 AT_MOST:
                  不管兒子申請 MATCH_PARENT 還是 WRAP_CONTENT:告訴子孫們手頭緊Mode=AT_MOST,Size=size
         不知道啥情況,但是手動有錢 UNSPECIFIED:
                 不管兒子申請 MATCH_PARENT 還是 WRAP_CONTENT:告訴子孫們家裡不知道啥情況Mode=UNSPECIFIED,Size=size      
複製程式碼

自己絕育了View,祖宗傳下來過日子之法 (詳情resolveSize)

看家裡情況
            要你都用掉 EXACTLY: 
                  都用光
        家裡手頭緊不能超過 AT_MOST:
                  自己用多少算多少吧,不超過就行
         不知道啥情況,但是手動有錢 UNSPECIFIED:
                   沒限制,自己用多少算多少
複製程式碼
  1. mView.layout 遞迴View樹 根據viewGroup的layout規則設定所有子View的位置
    3.1 ViewGroup必須重寫onLayout,根據排列規則以及measure測量的寬高呼叫child.layout對子View進行設定
    3.2 View的layout中呼叫setFrame完成對mLeft、mTop、mRight和mBottom賦值
    總結:所有的View都得靠父佈局通過自己的margin等相對規則確定位置
  2. mView.draw 遞迴View樹繪製所有view到canvas上
    4.1 draw()控制整體繪製流程,可被重寫
    4.2 drawBackground()繪製背景,不可被重寫
    4.3 ondraw()繪製主體,可被重寫
    4.4 dispatchDraw() 主要給ViewGroup重寫分配給子View繪製,可重寫
    4.5 繪製滾動條和前景 在6.0合併到onDrawForeground(),可被重寫

ps:其實還有一個draw被ViewGroup通過drawChild呼叫,這個draw拿到的canvas是完整的父View的canvas,應用view動畫的矩陣之後在整個canvas裡裁剪對應view位置和大小的一塊canvas交給處理view自身繪製流程的draw方法

應用事件分發流程

通過View樹傳遞觸控事件,一旦有View消費掉事件就不會繼續向下傳遞,如果沒有消費掉就會往上拋交給父View處理。down事件被View消費之後,後面連續的事件都會交給它處理。

  • Activity事件分發
  1. phoneWindow將事件回撥Activity的dispatchTouchEvent()分發事件
  2. 如果Event是ACTION_DOWN的情況下會接著觸發onUserInteraction方法。
  3. 分發給mContentParent的子View處理事件
  4. 若子view攔截了事件(返回true)則Activity.onTouchEvent方法就不會執行。
  • ViewGroup事件分發
  1. dispatchTouchEvent 分發處理事件
  2. 通過disallowIntercept先判斷子View是否不讓ViewGroup攔截事件
  3. onInterceptTouchEvent()判斷當前ViewGroup是否需要攔截該事件
  4. 不攔截就直接dispatchTransformedTouchEvent()分發給子View處理,如果子View沒有處理就繼續交給自己邏輯
  5. 攔截就交給自己處理,交給super.dispatchTochEvent()處理
  • View事件分發
  1. dispatchTouchEvent 分發處理事件
  2. 如果view可用則呼叫mOnTouchListener(可消費調觸控事件)
  3. 在onTochEvent中處理事件
    3.1 如果view是disable且是cliable 則直接消費調事件
    3.2 如果是enable且discliable直接返回 不消費事件
    3.3 如果是enable且cliable則處理消費事件邏輯,如up事件就會觸發clickListner

總結:view要消費調Down事件才會接受到後續事件,而事件達到view時可以被責任鏈裡的ViewGroup攔截掉,一旦攔截掉後續的任何一個事件對View來說就不完整了,所以為了保證view可以獲取所有事件,可以宣告不讓他們攔截自己的事件。自己也不要在onTouchListner裡隨便攔截會影響onTouchEvent的正常處理的。如果沒有view處理就會向上繼續傳播處理

部分學習自老羅的部落格

相關文章