1. 程式人生 > >一篇文章看明白 Android 從點選應用圖示到介面顯示的過程

一篇文章看明白 Android 從點選應用圖示到介面顯示的過程

Android - Activity 啟動過程

概述

從點選桌面應用圖示到應用顯示的過程我們再熟悉不過了,下面我們來分析下這個過程都做了什麼。

本文主要對以下問題分析:

  • ActivityThread 是什麼,它是一個執行緒嗎,如何被啟動的?
  • ActivityClientRecord 與 ActivityRecord 是什麼?
  • Context 是什麼,ContextImpl,ContextWapper 是什麼?
  • Instrumentation 是什麼?
  • Application 是什麼,什麼時候建立的,每個應用程式有幾個 Application?
  • 點選 Launcher 啟動 Activity 和應用內部啟動 Activity 的區別?
  • Activity 啟動過程,onCreate(),onResume() 回撥時機及具體作用?

Launcher

如不瞭解 Android 是如何從開機到 Launcher 啟動的過程,請先閱讀Android - 系統啟動過程

這裡寫圖片描述

我們知道 Android 系統啟動後已經啟動了 Zygote,ServiceManager,SystemServer 等系統程序;ServiceManager 程序中完成了 Binder 初始化;SystemServer 程序中 ActivityManagerService,WindowManagerService,PackageManagerService 等系統服務在 ServiceManager 中已經註冊;最後啟動了 Launcher 桌面應用。

其實 Launcher 本身就是一個應用程式,執行在自己的程序中,我們看到的桌面就是 Launcher 中的一個 Activity。

應用安裝的時候,通過 PackageManagerService 解析 apk 的 AndroidManifest.xml 檔案,提取出這個 apk 的資訊寫入到 packages.xml 檔案中,這些資訊包括:許可權、應用包名、icon、apk 的安裝位置、版本、userID 等等。packages.xml 檔案位於系統目錄下/data/system/packages.xml。

同時桌面 Launcher 會為安裝過的應用生成不同的應用入口,對應桌面上的應用圖示,下面分析點選應用圖示的到應用啟動的過程。

點選 Launcher 中應用圖示

這裡寫圖片描述

點選 Launcher 中應用圖示將會執行以下方法

Launcher.startActivitySafely()
Launcher.startActivity()
//以上兩個方法主要是檢查將要開啟的 Activity 是否存在

Activity.startActivity()
//這段程式碼大家已經很熟悉,經常開啟 Activity 用的就是這個方法

Activity.startActivityForResult()
//預設 requestCode = -1,也可通過呼叫 startActivityForResult() 傳入 requestCode。 
//然後通過 MainThread 獲取到 ApplicationThread 傳入下面方法。

Instrumentation.execStartActivity()
//通過 ActivityManagerNative.getDefault() 獲取到 ActivityManagerService 的代理為程序通訊作準備。

ActivityManagerNative.getDefault().startActivity()
ActivityManagerProxy.startActivity()
//呼叫代理物件的 startActivity() 方法,傳送 START_ACTIVITY_TRANSACTION 命令。

在 system_server 程序中的服務端 ActivityManagerService 收到 START_ACTIVITY_TRANSACTION 命令後進行處理,呼叫 startActivity() 方法。

ActivityManagerService.startActivity() -> startActivityAsUser(intent, requestCode, userId)
//通過 UserHandle.getCallingUserId() 獲取到 userId 並呼叫 startActivityAsUser() 方法。

ActivityStackSupervisor.startActivityMayWait() -> resolveActivity()
//通過 intent 建立新的 intent 物件,即使之前 intent 被修改也不受影響。 然後呼叫 resolveActivity()。
//然後通過層層呼叫獲取到 ApplicationPackageManager 物件。

PackageManagerService.resolveIntent() -> queryIntentActivities()
//獲取 intent 所指向的 Activity 資訊,並儲存到 Intent 物件。

PackageManagerService.chooseBestActivity()
//當存在多個滿足條件的 Activity 則會彈框讓使用者來選擇。

ActivityStackSupervisor.startActivityLocked()
//獲取到呼叫者的程序資訊。 通過 Intent.FLAG_ACTIVITY_FORWARD_RESULT 判斷是否需要進行 startActivityForResult 處理。 
//檢查呼叫者是否有許可權來呼叫指定的 Activity。 
//建立 ActivityRecord 物件,並檢查是否執行 App 切換。

ActivityStackSupervisor.startActivityUncheckedLocked() -> startActivityLocked()
//進行對 launchMode 的處理[可參考 Activity 啟動模式],建立 Task 等操作。
//啟動 Activity 所在程序,已存在則直接 onResume(),不存在則建立 Activity 並處理是否觸發 onNewIntent()。

ActivityStack.resumeTopActivityInnerLocked()
//找到 resume 狀態的 Activity,執行 startPausingLocked() 暫停該 Activity,同時暫停所有處於後臺棧的 Activity,找不到 resume 狀態的 Activity 則回桌面。
//如果需要啟動的 Activity 程序已存在,直接設定 Activity 狀態為 resumed。 呼叫下面方法。

ActivityStackSupervisor.startSpecificActivityLocked()
//程序存在呼叫 realStartActivityLocked() 啟動 Activity,程序不存在則呼叫下面方法。

fork 新程序

從 Launcher 點選圖示,如果應用沒有啟動過,則會 fork 一個新程序。建立新程序的時候,ActivityManagerService 會儲存一個 ProcessRecord 資訊,Activity 應用程式中的AndroidManifest.xml 配置檔案中,我們沒有指定 Application 標籤的 process 屬性,系統就會預設使用 package 的名稱。每一個應用程式都有自己的 uid,因此,這裡 uid + process 的組合就可以為每一個應用程式建立一個 ProcessRecord。每次在新建新程序前的時候會先判斷這個 ProcessRecord 是否已存在,如果已經存在就不會新建程序了,這就屬於應用內開啟 Activity 的過程了。

ActivityManagerService.startProcessLocked()
//程序不存在請求 Zygote 建立新程序。 建立成功後切換到新程序。

程序建立成功切換至 App 程序,進入 app 程序後將 ActivityThread 類載入到新程序,並呼叫 ActivityThread.main() 方法

ActivityThread.main()
//建立主執行緒的 Looper 物件,建立 ActivityThread 物件,ActivityThread.attach() 建立 Binder 通道,開啟 Looper.loop() 訊息迴圈。

ActivityThread.attach()
//開啟虛擬機器各項功能,建立 ActivityManagerProxy 物件,呼叫基於 IActivityManager 介面的 Binder 通道 ActivityManagerProxy.attachApplication()。

ActivityManagerProxy.attachApplication()
//傳送 ATTACH_APPLICATION_TRANSACTION 命令

此時只建立了應用程式的 ActivityThread 和 ApplicationThread,和開啟了 Handler 訊息迴圈機制,其他的都還未建立, ActivityThread.attach(false) 又會最終到 ActivityMangerService 的 attachApplication,這個工程其實是將本地的 ApplicationThread 傳遞到 ActivityMangerService。然後 ActivityMangerService 就可以通過 ApplicationThread 的代理 ApplicationThreadProxy 來呼叫應用程式 ApplicationThread.bindApplication,通知應用程式的 ApplicationThread 已和 ActivityMangerService 繫結,可以不借助其他程序幫助直接通訊了。此時 Launcher 的任務也算是完成了。

在 system_server 程序中的服務端 ActivityManagerService 收到 ATTACH_APPLICATION_TRANSACTION 命令後進行處理,呼叫 attachApplication()。

ActivityMangerService.attachApplication() -> attachApplicationLocked()
//首先會獲取到程序資訊 ProcessRecord。 繫結死亡通知,移除程序啟動超時訊息。 獲取到應用 ApplicationInfo 並繫結應用 IApplicationThread.bindApplication(appInfo)。
//然後檢查 App 所需元件。
  • Activity: 檢查最頂層可見的 Activity 是否等待在該程序中執行,呼叫 ActivityStackSupervisor.attachApplicationLocked()。
  • Service:尋找所有需要在該程序中執行的服務,呼叫 ActiveServices.attachApplicationLocked()。
  • Broadcast:檢查是否在這個程序中有下一個廣播接收者,呼叫 sendPendingBroadcastsLocked()。

此處討論 Activity 的啟動過程,只討論 ActivityStackSupervisor.attachApplicationLocked() 方法。

ActivityStackSupervisor.attachApplicationLocked() -> realStartActivityLocked()
//將該程序設定為前臺程序 PROCESS_STATE_TOP,呼叫 ApplicationThreadProxy.scheduleLaunchActivity()。

ApplicationThreadProxy.scheduleLaunchActivity()
//傳送 SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION 命令

傳送送完 SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION 命令,還會發送 BIND_APPLICATION_TRANSACTION 命令來建立 Application。

ApplicationThreadProxy.bindApplication()
//傳送 BIND_APPLICATION_TRANSACTION 命令

App 程序初始化

在 app 程序中,收到 BIND_APPLICATION_TRANSACTION 命令後呼叫 ActivityThread.bindApplication()。

ActivityThread.bindApplication()
//快取 Service,初始化 AppBindData,傳送訊息 H.BIND_APPLICATION。

ApplicationThreadProxy.bindApplication(…) 會傳來這個應用的一些資訊,如ApplicationInfo,Configuration 等,在 ApplicationThread.bindApplication 裡會待資訊封裝成A ppBindData,通過

sendMessage(H.BIND_APPLICATION, data)

將資訊放到應用裡的訊息佇列裡,通過 Handler 訊息機制,在 ActivityThread.handleMeaasge 裡處理 H.BIND_APPLICATION 的資訊,呼叫 AplicationThread.handleBindApplication。

handleBindApplication(AppBindData data) {
    Process.setArgV0(data.processName);//設定程序名
    ...
    //初始化 mInstrumentation
    if(data.mInstrumentation!=null) {
        mInstrumentation = (Instrumentation) cl.loadClass(data.instrumentationName.getClassName()).newInstance();
    } else {
        mInstrumentation = new Instrumentation();
    }
    //建立Application,data.info 是個 LoadedApk 物件。
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
    //呼叫 Application 的 onCreate()方法。
    mInstrumentation.callApplicationOnCreate(app);
}

public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {

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

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

    //此時新建一個 Application 的 ContextImpl 物件,
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

    //通過在 handleBindApplication 建立的 mInstrumentation 物件新建一個 Application 物件,同時進行 attach。
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
    appContext.setOuterContext(app);
}

//設定程序名,獲取 LoadedApk 物件,建立 ContextImpl 上下文
//LoadedApk.makeApplication() 建立 Application 物件,呼叫 Application.onCreate() 方法。

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)  {
    //例項化 Application
    Application app = (Application)clazz.newInstance();     

    // Application 和 context繫結
    app.attach(context);    
    return app;
}
//attach 就是將新建的 ContextImpl 賦值到 mBase,這個 ContextImpl 物件就是所有Application 內 Context 的具體實現,同時賦值一些其他的資訊如 mLoadedApk。
final void attach(Context context) {    
    mBase = base;  
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

這時 Application 就建立好了,這點很重要,很多資料裡說 Application 是在performLaunchActivity() 裡建立的,因為 performLaunchActivity() 也有mInstrumentation.newApplication 這個呼叫,newApplication() 函式中可看出會先判斷是否以及建立了 Application,如果之前已經建立,就返回已建立的 Application 物件。

Activity 啟動

上面 fork 程序時會發送 SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION 命令,在 app 程序中,收到 SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION 命令後呼叫 ApplicationThread.scheduleLaunchActivity()。

ApplicationThread.scheduleLaunchActivity()
//傳送訊息 H.LAUNCH_ACTIVITY。

sendMessage(H.LAUNCH_ACTIVITY, r);

ActivityThread.handleLaunchActivity()
//最終回撥目標 Activity 的 onConfigurationChanged(),初始化 WindowManagerService。
//呼叫 ActivityThread.performLaunchActivity()

ActivityThread.performLaunchActivity() {
    //類似 Application 的建立過程,通過 classLoader 載入到 activity.
    activity = mInstrumentation.newActivity(classLoader, 
               component.getClassName(), r.intent);
    //因為 Activity 有介面,所以其 Context 是 ContextThemeWrapper 型別,但實現類仍是ContextImpl.
    Context appContext = createBaseContextForActivity(r, activity);
    activity.attach(context,mInstrumentation,application,...);
    //與 Window 進行關聯

    //attach 後呼叫 activity 的 onCreate()方法。
    mInstrumentation.callActivityOnCreate(activity,...)

}
//在ActivityThread.handleLaunchActivity裡,接著呼叫

Activity.performCreate() -> onCreate()
//最終回撥目標 Activity 的 onCreate()。

Activity.setContentView()
//設定 layout 佈局

ActivityThread.performResumeActivity()
//最終回撥目標 Activity 的 onResume()。

總結

Activity 的整體啟動流程如圖所示:

這裡寫圖片描述

  • ActivityThread 是什麼,它是一個執行緒嗎,如何被啟動的?

它不是一個執行緒,它是執行在 App 程序中的主執行緒中的一個方法中。當 App 程序建立時會執行 ActivityThread.main(),ActivityThread.main() 首先會建立 Looper 執行 Looper.prepareMainLooper();然後建立 ActivityThread 並呼叫 ActivityThread.attach() 方法告訴 ActivityManagerService 我們建立了一個應用 並將 ApplicationThread 傳給 ActivityManagerService;最後呼叫 Looper.loop()。

  • ActivityClientRecord 與 ActivityRecord 是什麼?

記錄 Activity 相關資訊,比如:Window,configuration,ActivityInfo 等。
ActivityClientRecord 是客戶端的,ActivityRecord 是 ActivityManagerService 服務端的。

  • Context 是什麼,ContextImpl,ContextWapper 是什麼?

Context 定義了 App 程序的相關環境,Context 是一個介面,ContextImpl 是子類,ContextWapper 是具體實現。

應用資源是在 Application 初始化的時候,也就是建立 Application,ContextImpl 的時候,ContextImpl 就包含這個路徑,主要就是對就是 ResourcesManager 這個單例的引用。

可以看出每次建立 Application 和 Acitvity 以及 Service 時就會有一個 ContextImpl 例項,ContentProvider 和B roadcastReceiver 的 Context 是其他地方傳入的。

所以 Context 數量 = Application 數量 + Activity 數量 + Service 數量,單程序情況下 Application 數量就是 1。

  • Instrumentation 是什麼?

管理著元件Application,Activity,Service等的建立,生命週期呼叫。

  • Application 是什麼,什麼時候建立的,每個應用程式有幾個 Application?

Application 是在 ActivityThread.handleBindApplication() 中建立的,一個程序只會建立一個 Application,但是一個應用如果有多個程序就會建立多個 Application 物件。

  • 點選 Launcher 啟動 Activity 和應用內部啟動 Activity 的區別?

點選 Launcher 時會建立一個新程序來開啟 Activity,而應用內開啟 Activity,如果 Activity 不指定新程序,將在原來程序開啟,是否開啟新程序實在 ActivityManagerService 進行控制的,上面分析得到,每次開啟新程序時會儲存程序資訊,預設為 應用包名 + 應用UID,開啟 Activity 時會檢查請求方的資訊來判斷是否需要新開程序。Launcher 開啟 Activity 預設 ACTIVITY_NEW_TASK,新開一個 Activity 棧來儲存 Activity 的資訊。

  • Activity 啟動過程,onCreate(),onResume() 回撥時機及具體作用?

Activity.onCreate() 完成了 App 程序,Application,Activity 的建立,呼叫 setContentView() 給 Activity 設定了 layout 佈局。

Activity.onResume() 完成了 Activity 中 Window 與 WindowManager 的關聯,並對所有子 View 進行渲染並顯示。

參考資料

更多文章

歡迎長按下圖 -> 識別圖中二維碼
或者 掃一掃 關注我的公眾號

這裡寫圖片描述

相關推薦

文章明白 Android 應用圖示介面顯示過程

Android - Activity 啟動過程 概述 從點選桌面應用圖示到應用顯示的過程我們再熟悉不過了,下面我們來分析下這個過程都做了什麼。 本文主要對以下問題分析: ActivityThread 是什麼,它是一個執行緒嗎,如何被啟動的? Act

文章明白 Android 圖形系統 Surface 與 SurfaceFlinger 之間的關係

原址 Android - SurfaceFlinger 圖形系統 概述 Android 系統啟動過程 Activity 建立過程 Activity 與 Window 與 View 之間的關係 通過前面的知識我們知道了,Android 系統從按下開機鍵到桌面,從桌面點

文章Android學習最佳路線

為什麼中高階Android程式設計師不多呢?這是一個問題,我不好回答,但是我想寫一篇文章來描述下Android的學習路線,期望可以幫助更多的Android程式設計師提升自己。 作者:來源:Android開發中文站|2015-11-12 10:40  收藏   分享 前言

文章懂物體檢測的發展脈絡 轉

現實生活 有關 攝像頭 層次 contex 人的 圖像特征 其實在 展示 轉 https://zhuanlan.zhihu.com/p/28399320 第一,什麽是物體檢測,如何去評價一個物體裏系統的好壞。 第二,物體檢測整個的框架是怎麽樣的?它一般包含了圖像的分類

小程序社交立減金正式開放!文章懂它

開放 則無 掃碼 創建 ucs 3D 功夫 無法 class 2018微信公開課PRO現場,微信小程序團隊宣布,小程序立減金能力正式上線。 什麽是社交立減金? 這是一款能夠幫助商家快速獲取社交、裂變傳播屬性的小程序經營工具——商家應用了這一能力後,用戶通過支付、掃碼等場景就

微信小程序社交立減金正式開放!文章懂它(開通微信免充值代金券)

社交 秘鑰 一份 直接 log 平臺 幫助 可選 對賬 2018微信公開課PRO現場,微信小程序團隊宣布,小程序立減金能力正式上線。 什麽是社交立減金? 這是一款能夠幫助商家快速獲取社交、裂變傳播屬性的小程序經營工具——商家應用了這一能力後,用戶通過支付、掃碼等場景就能參

文章懂分布式架構的演進過程,你也能輕松上手

Java Java開發 Java編程 一.分布式架構的發展歷史1946年,世界上第一臺電子計算機在美國的賓夕法尼亞大學誕生,它的名字是:ENICAC ,這臺計算機的體重比較大,計算速度也不快,但是而代表了計算機時代的到來,再以後的互聯網的發展中也有基礎性的意義。計算機的組成是有五部分完成的,分別是

文章懂有關iOS開發語言的一切!(轉載)

數字 uikit 入門 ios 函數 重要 代理 作用 iphone 今天小編給大家帶來一些關於ios開發語言的相關知識層面的理解要點~ 前言 iOS開發語言有哪些?iOS開發語言主要包括什麽?iOS開發語言具體怎麽學習?今天重點介紹一下: iOS開發語言主要包括:C語言基

文章懂linux的2>$1

2 > $1 今天看到群裡有人在問這個問題,我這裡恰好沒有寫文章。所以這裡整理一下。 1在檔案裡面是標準輸出,stdout 2是標準錯誤輸出。stderr 0是標準輸入,stdin 這裡必須得有演示,否則很多人看不懂。 [email protec

文章明白CORS跨域

面試問到資料互動的時候,經常會問跨域如何處理。大部分人都會回答JSONP,然後面試官緊接著就會問:“JSONP缺點是什麼啊?”這個時候坑就來了,如果面試者說它支援GET方式,然後面試官就會追問,那如果POST方式傳送請求怎麼辦?基礎紮實一些的面試者會說,使用CORS跨域,不紮實的可能就搖搖頭了。 這還沒結束

文章懂無伺服器計算的本質與未來

無伺服器計算,或者功能即服務(Functions-as-a-Service,以下簡稱FaaS),未來將會改變我們的產業鏈,凡是基於它的企業或者組織,最終會受益於它在價格、人力,以及上市時間等方面帶來的競爭優勢。 許多無伺服器應用程式,未來將會對FaaS和其他第三方提供的服務進行組合,以便提供功能預管理和狀

文章懂TPCx-BB(大資料基準測試工具)原始碼

TPCx-BB是大資料基準測試工具,它通過模擬零售商的30個應用場景,執行30個查詢來衡量基於Hadoop的大資料系統的包括硬體和軟體的效能。其中一些場景還用到了機器學習演算法(聚類、線性迴歸等)。為了更好地瞭解被測試的系統的效能,需要對TPCx-BB整個測試流程深入瞭解。本文詳細分析了整個TPCx

文章懂rabbitMQ

看到一篇寫得比較好的文章,轉載已做記錄。 轉載於:https://www.cnblogs.com/tohxyblog/p/7256343.html 一、rabbitMQ是什麼:   RabbitMQ,遵循AMQP協議,由內在高併發的erlanng語言開發,用在實時的對可靠性要求比較高的訊息傳

【朝花夕拾】文章搞懂Android跨程序通訊

前言        只要是面試中高階工程師崗位,Android跨程序通訊就是最受面試官青睞的知識點。Android系統的執行由大量相互獨立的程序相互協助來完成的,所以Android程序間通訊問題,是做好Android開發高階工程師必須要跨過的一道坎。如果您還對這方面的

張圖明白CAS單登入原理

最近工作上用到了CAS,研究了下,折騰的心累.關於CAS單點登入的原理或者說認證流程,網上查到的資料上大部分都扯得不是很清晰,看的雲裡霧裡.所以就花了些時間專門去整理了下畫了這張時序圖,用的是一個線上的工具,對UML的支援不怎麼完善,因此畫出來的就顯得不是很專業

文章懂iOS程式碼塊Block

iOS程式碼塊Block 概述 程式碼塊Block是蘋果在iOS4開始引入的對C語言的擴充套件,用來實現匿名函式的特性,Block是一種特殊的資料型別,其可以正常定義變數、作為引數、作為返回值,特殊地,Block還可以儲存一段程式碼,在需要的時候呼叫,目前Block已經廣泛應用於iOS開發

文章懂事務

開發十年,就只剩下這套架構體系了! >>>   

文章懂大資料分析就業前景及職能定位

開發十年,就只剩下這套架構體系了! >>>   

文章懂 Python iterable, iterator 和 generato

Python 中的 iterable, iterator 以及 generator,一直是非常親密但是難

文章詳解大資料技術和應用場景

什麼是大資料 說起大資料,估計大家都覺得只聽過概念,但是具體是什麼東西,怎麼定義,沒有一個標準的東西,因為在我們的印象中好像很多公司都叫大資料公司,業務形態則有幾百種,感覺不是很好理解,所以我建議還是從字面上來理解大資料,在維克托邁爾-舍恩伯格及肯尼斯庫克耶編寫的《大資料時代》提到了大資料的4個特徵: