主頁 > 移動端開發 > Android系統開機到Launcher啟動流程分析

Android系統開機到Launcher啟動流程分析

2021-02-22 10:29:24 移動端開發

常見基礎問題:

SystemServer系統服務行程是如何創建的?Launcher行程如何被創建的?
是由Zygote行程fork而來

Launcher啟動入口在哪兒?
ActivityManagerService的systemReady函式就是啟動Launcher的入口,

系統如何識別Launcher應用?
android.intent.category.HOME

如何開發一個桌面Launcher應用?
android.intent.category.HOME;
android.intent.category.DEFAUlT,

Android應用行程的的入口類?
ActivityThread

為什么Activity必須在清單檔案中注冊?
ActivityStarter會做各種啟動前檢查,

一、Android系統架構及開機流程

Google官方提供的經典分層架構圖:
在這里插入圖片描述
從下往上依次分為5層:

  • Linux內核層
  • HAL層(硬體抽象層)
  • 系統運行庫層(系統Native庫和Android運行時環境)
  • FramWork層(Java框架層)
  • Application層(包括Systema Apps和三方App)

Google提供的5層架構圖很經典,但為了更進一步透視Android系統架構,本文更多的是以行程的視角,以分層的架構來詮釋Android系統的全貌,闡述Android內部的環環相扣的內在聯系,如下為:系統啟動架構圖
在這里插入圖片描述
首先,關于Android手機開機的程序,Loader層:

Boot ROM: 當手機處于關機狀態時,長按Power鍵開機,引導芯片開始從固化在ROM里的預設代碼開始執行,然后加載引導程式到RAM;
Boot Loader:這是啟動Android系統之前的引導程式,主要是檢查RAM,初始化硬體引數,拉起Android OS,

圖解: Android系統啟動程序由上圖從下往上的一個程序是由Boot Loader引導開機,然后依次進入 -> Linux Kernel -> Native -> Framework -> App,接來下簡要說說每個程序:

Linux內核層

Android平臺的基礎是Linux內核,比如ART虛擬機最終呼叫底層Linux內核來執行功能,Linux內核的安全機制為Android提供相應的保障,也允許設備制造商為內核開發硬體驅動程式,

啟動Kernel的swapper行程(pid=0):該行程又稱為idle行程, 系統初始化程序Kernel由無到有開創的第一個行程, 用于初始化行程管理、記憶體管理,加載Display,Camera Driver,Binder Driver等相關作業;
啟動kthreadd行程(pid=2):是Linux系統的內核行程,會創建內核作業執行緒kworkder,軟中斷執行緒ksoftirqd,thermal等內核守護行程,kthreadd行程是所有內核行程的鼻祖,

硬體抽象層 (HAL)

硬體抽象層 (HAL) 提供標準介面,HAL包含多個庫模塊,其中每個模塊都為特定型別的硬體組件實作一組介面,比如WIFI/藍牙模塊,當框架API請求訪問設備硬體時,Android系統將為該硬體加載相應的庫模塊,

系統運行庫層

每個應用都在其自己的行程中運行,都有自己的虛擬機實體,ART通過執行DEX檔案可在設備運行多個虛擬機,DEX檔案是一種專為Android設計的位元組碼格式檔案,經過優化,使用記憶體很少,ART主要功能包括:預先(AOT)和即時(JIT)編譯,優化的垃圾回收(GC),以及除錯相關的支持,

這里的Native系統庫主要包括init范訓來的用戶空間的守護行程、HAL層以及開機影片等,啟動init行程(pid=1),是Linux系統的用戶行程init行程是所有用戶行程的鼻祖

Framework層:

Zygote行程,是由init行程通過決議init.rc檔案后fork生成的,Zygote行程主要包含:

加載ZygoteInit類,注冊Zygote Socket服務端套接字
加載虛擬機
提前加載類preloadClasses
提前加載資源preloadResouces

SystemServer系統服務行程,是由Zygote行程fork而來,System Server是Zygote范訓的第一個行程,System Server負責啟動和管理整個Java framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服務,

Media Server行程,是由init行程fork而來,負責啟動和管理整個C++ framework,包含AudioFlinger,Camera Service等服務,

Application層

Zygote行程范訓出的第一個App行程是Launcher這是用戶看到的桌面App
Zygote行程還會創建Browser,Phone,Email等App行程,每個App至少運行在一個行程上,
所有的App行程都是由Zygote行程fork生成的,

二、Android系統中極其重要的行程:

init行程, Zygote行程, SystemServer行程, ServiceManager行程,

2.1.init行程

Init行程是Android啟動的第一個行程,行程號為1(pid=1),是Android 的系統啟動的核心行程,主要用來創建Zygote、屬性服務等, init.cpp 中的main 函式,是init行程的入口函式,原始碼主要存在\system\core\init目錄下,

重點:它是Linux系統中用戶空間的第一個行程,由于Android是基于Linux內核的,所以init也是Android系統中用戶空間的第一個行程,

在這里插入圖片描述

init行程會范訓出ueventd、logd、healthd、installd、adbd、lmkd等用戶守護行程(守護行程一般以d結尾);
init提供property service(屬性服務)來管理Android系統的屬性,
init行程還啟動servicemanager(binder服務管家)bootanim(開機影片)等重要服務
init行程范訓出Zygote行程,Zygote行程是Android系統的第一個Java行程(即虛擬機行程),Zygote是所有Java行程的父行程,Zygote行程本身是由init行程范訓而來的,

Linux Kernel完成系統設定后,會首先在系統中尋找init.rc檔案,并啟動init行程,

由于init中都是C語言代碼,看不懂,不深入看原始碼了,簡單了解下啟動zygote的部分,在init.zygote.rc檔案中,zygote服務定義如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

通過init_parser.cpp完成整個service決議作業,此處就不詳細展開講決議程序,該程序主要作業是:

創建一個名叫”zygote”的service結構體;
創建一個用于socket通信的socketinfo結構體;
創建一個包含4個onrestart的action結構體,

Zygote服務會隨著main class的啟動而啟動,退出后會由init重啟zygote,即使多次重啟也不會進入recovery模式,zygote所對應的可執行檔案是/system/bin/app_process,通過呼叫pid =fork()創建子行程,通過execve(svc->args[0], (char**)svc->args, (char**) ENV),進入App_main.cpp的main()函式,故zygote是通過fork和execv共同創建的

2.2.Zygote行程

在Zygote行程創建完成之后,會進入java世界,即ZygoteInit.java,

問:我們知道Java呼叫C++可以通過JNI,那么C++是如何轉到Java的,也就是說ZygoteInit.java這個類是如何被呼叫的?
答:在java中,class檔案是由ClassLoader來加載的,但實際上,ClassLoader在加載java檔案的程序中,也是通過C++來完成的(Bootstrp loader就是用C++寫的,具體可以去看java類加載程序及機制,Java類加載器ClassLoader總結),所以C++如果想要訪問java檔案的話,是非常輕松的,如下:
在這里插入圖片描述
接下里看ZygoteInit.java都做了些什么
在這里插入圖片描述
這里第一步預加載的有系統的class檔案,系統的資源檔案,系統的動態庫,因為我們所開發的應用在運行時會使用到大量的系統資源,如果這些資源在app啟動的時候才加載,勢必會造成app啟動緩慢,而如果在Android系統啟動的時候就將這些資源提前預加載,就可以達到提高啟動app速度,

第三步這里的Socket服務是用來接受來自ActivityManagerService申請行程創建的請求的,然后就會進入到阻塞狀態,等待連接,

SystemServer行程創建后會做什么,看一下SystemServer.java這個類(抽取最核心的代碼):
在這里插入圖片描述
在SystemServer的入口mian方法中,啟動了非常多的服務,這些服務都交給SystemServerManager來管理,
在這里插入圖片描述
其中其它服務一共有90+,這些服務都是通過startService方式啟動,并注冊到SystemServerManager當中的,如:
在這里插入圖片描述

這些系統服務就構成了Android的FramWork層,為上層的app開發和運行提供了保障,所以SystemServer這個行程是在系統的啟動流程中,我們需要重點了解的,

當這些服務啟動完成之后;其中的ActivityManagerService最侄訓呼叫systemReady()方法,
在這里插入圖片描述
注意systemReady這個方法,注釋的意思大概是:我們是時候去通知ActivityManager去啟動第三方應用了,當三方應用真的可以運行的時候會通知我們,以便我們完成初始化,括號里面的內容大概是:在它(三方應用)啟動之前會先啟動launcher應用,

Android 系統啟動流程總結:

第一步:手機開機后,引導芯片啟動,引導芯片開始從固化在ROM里的預設代碼執行,加載引導程式到到RAM,BootLoader檢查RAM,初始化硬體引數等功能;

第二步:硬體等引數初始化完成后,進入到Kernel層,Kernel層主要加載一些硬體設備驅動,初始化行程管理等操作,在Kernel中首先啟動swapper行程(pid=0),用于初始化行程管理、內管管理、加載Driver等操作,再啟動kthread行程(pid=2),這些linux系統的內核行程,kthread是所有內核行程的鼻祖;

第三步:Kernel層加載完畢后,硬體設備驅動與HAL層進行互動,初始化進程管理等操作會啟動init行程 ,這些在Native層中;

第四步:init行程(pid=1,init行程是所有行程的鼻祖,第一個啟動)啟動后,會啟動adbd,logd等用戶守護行程,并且會啟動servicemanager(binder服務管家)等重要服務,同時范訓出zygote行程,這里屬于C++ Framework,代碼為C++程式;

第五步:zygote行程是由init行程決議init.rc檔案后fork生成,它會加載虛擬機,啟動System Server(zygote范訓的第一個行程);SystemServer負責啟動和管理整個Java Framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服務;

第六步:zygote同時會啟動相關的APP行程,它啟動的第一個APP行程為Launcher,然后啟動Email,SMS等行程,所有的APP行程都有zygote fork生成,

到這里,系統開機到桌面應用準備就完成了,接下來就是launcher應用啟動流程分析,

三、Launcher啟動流程

3.1.Launcher啟動流程關鍵類介紹:

ActivityManagerService
負責管理四大組件和行程,包括生命周期和狀態切換,它的systemReady()方法,正是launcher應用啟動的入口,

ActivityTaskManagerService
把原先在ActivityManagerService中負責Activity管理和調度等作業轉移到了這里,ActivityTaskManagerService是Android10中新增的,

RootActivityContainer:
呼叫packageManagerService中去查詢手機系統中已安裝的所有的應用,哪一個符合launcher標準,且得到一個Intent物件,并交給ActivityStarter,

ActivityStarter
做啟動之前的各項檢查,比如Activity是否有在清單檔案注冊,Class檔案是否存在等等,

ActivityRecord:
在Server端對activity的一種映射,記錄和存盤activity的資訊,

TaskRecord
記錄一個或多個ActivityRecord的實體

ActivityStack:
應用的任務堆疊的管理者

ActivityStackSupervisor
負責所有Activity堆疊的管理,包括launcher和非launcher應用,

ProcessList:
把原先在AMS中啟動行程的作業轉移到這里,是Android10中新增的,

Instrumentation:
負責呼叫Activity和Application生命周期,

ActivityTaskManagerInternal:
是由ActivityTaskManagerService對外提供的一個抽象類,真正的實作是在 ActivityTaskManagerService#LocalService

ActivityThread:
管理應用程式行程中主執行緒的執行

TransactionExecutor:
主要作用是執行ClientTransaction

ClientLifecycleManager:
生命周期的管理呼叫

3.2.launcher啟動流程分析:

Launcher的啟動由三部分啟動:

1.SystemServer完成啟動Launcher Activity(符合launcher應用的最佳Activity)的呼叫

2.Zygote fork出launcher行程,

3.ActivityThread的main()方法,完成最終Launcher的onCreate操作

3.2.1.第一階段:SystemServer啟動Launcher應用呼叫階段

呼叫堆疊如下:
在這里插入圖片描述

前面我們知道了ActivityManagerService的systemReady()正是Launcher啟動的入口,在其中會去呼叫startHomeOnAllDisplays()來啟動Launcher.

[ActivityManagerService.java]
public void systemReady(final Runnable goingCallback, TimingsTraceLog 
traceLog) {
    ...
    //啟動HomeActivity:本意是在所有的螢屏上啟動桌面應用,因為Android10.0開始是支持多螢屏的,比如手機螢屏,虛擬投屏,外界螢屏
    mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
    ...
}

這里的mAtmInternal.startHomeOnAllDisplays(),mAtmInternal是ActivityTaskManagerInternal,是一個抽象類

在這里插入圖片描述

真正的實作是在ActivityTaskManagerService的內部類LocalService中,

這里有個閱讀原始碼的技巧,一般以Internal結尾的抽象類,其實作類如下圖所示,都在內部類LocalService中,
在這里插入圖片描述
然后找到ActivityTaskManagerService的LocalService,它繼承自ActivityManagerInternal,確實有startHomeOnAllDisplays()方法,這里最終呼叫到RootActivityContainer 的startHomeOnDisplay()方法
在這里插入圖片描述
前面提到RootActivityContainer的作用是:呼叫packageManagerService中去查詢手機系統中已安裝的所有的應用,哪一個符合launcher標準,且得到一個Intent物件,并交給ActivityStarter

這里會一直呼叫到RootActivityContainer.startHomeOnDisplay()方法:


boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
    boolean fromHomeKey) {
    ...
     if (displayId == DEFAULT_DISPLAY) {
        //關鍵點1:構建一個category為CATEGORY_HOME的Intent,表明是HomeActivity(我覺得這個說法太含混,應該表明是構建一個符合launcher應用的Intent)
        homeIntent = mService.getHomeIntent();
        //關鍵點2:通過PMS從系統所用已安裝的參考中,找到一個符合homeIntent的Activity
        aInfo = resolveHomeActivity(userId, homeIntent); 
    } 
    ...
    //關鍵點3:啟動launcher
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
        displayId);
    return true;
}

獲取的displayId為DEFAULT_DISPLAY, 通過getHomeIntent 來構建一個category為CATEGORY_HOME的Intent,表明是一個符合launcher應用的Intent;然后通過resolveHomeActivity()從系統所用已安裝的參考中,找到一個符合該Intent的Activity,最終呼叫startHomeActivity()來啟動Activity

然后看下ActivityTaskManagerService.getHomeIntent()方法:主要就是構建一個
符合launcher應用的Intent,

Intent getHomeIntent() {
    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
    intent.setComponent(mTopComponent);
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    //不是生產模式,add一個CATEGORY_HOME
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
        intent.addCategory(Intent.CATEGORY_HOME);//表明是符合launcher的Intent
    }
    return intent;

RootActivityContainer.resolveHomeActivity()方法:


ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
    final int flags = ActivityManagerService.STOCK_PM_FLAGS;
    final ComponentName comp = homeIntent.getComponent(); //系統正常啟動時,component為null
    ActivityInfo aInfo = null;
    ...
        if (comp != null) {
            // Factory test.
            aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
        } else {
            //系統正常啟動時,走該流程
            final String resolvedType =
                    homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
            
            //resolveIntent做了兩件事:1.通過queryIntentActivities來查找符合HomeIntent需求Activities
            //2.通過chooseBestActivity找到最符合Intent需求的Activity資訊
            final ResolveInfo info = AppGlobals.getPackageManager()
                    .resolveIntent(homeIntent, resolvedType, flags, userId);
            if (info != null) {
                aInfo = info.activityInfo;
            }
        }
    ...
    aInfo = new ActivityInfo(aInfo);
    aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
    return aInfo;
}

通過Binder跨行程通知PackageManagerService從系統所用已安裝的參考中,找到一個符合HomeItent的Activity,

然后是ActivityStartController.startHomeActivity()方法:
這個類唯一的作用就是配置activity啟動前的一些資訊,并把這些資訊傳遞給ActivityStart去做啟動,


void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
    ....
    //關鍵點:obtainStarter方法回傳一個 ActivityStarter物件,它負責 Activity 的啟動
    mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
            .setOutActivity(tmpOutRecord)
            .setCallingUid(0)
            .setActivityInfo(aInfo)
            .setActivityOptions(options.toBundle())
            .execute(); // 關鍵點:execute()會觸發 ActivityStarter的execute方法
    mLastHomeActivityStartRecord = tmpOutRecord[0];
    final ActivityDisplay display =
            mService.mRootActivityContainer.getActivityDisplay(displayId);
    final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
    if (homeStack != null && homeStack.mInResumeTopActivity) {
        //如果home activity 處于頂層的resume activity中,則Home Activity 將被初始化,但不會被恢復(以避免遞回恢復),
        //并將保持這種狀態,直到有東西再次觸發它,我們需要進行另一次恢復,
        mSupervisor.scheduleResumeTopActivities();
    }
}

ActivityStarter.execute()方法:

int execute() {
    ...
    if (mRequest.mayWait) {
        return startActivityMayWait(...)
    } else {
         return startActivity(...) 
    }
    ...
}

由于obtainStarter方法沒有呼叫setMayWait的方法做任何配置,因此mRequest.mayWait為false,會走startActivity流程

ActivityStarter. startActivity()方法:
在這里插入圖片描述
這個方法主要用來做activity啟動之前的安全校驗

關鍵點:int err = ActivityManager.START_SUCCESS
在這里插入圖片描述
這里的caller,只有當應用行程創建完成之后,Server端才能拿到這個物件不為空的值,由于此時launcher應用的行程還未創建,所以此時caller物件一定為空,

還會做一些其它的校驗,包括后臺啟動activity,activity啟動權限等,其它如:
在這里插入圖片描述
接著會呼叫到ActivityStarter.startActivityUnchecked方法
在這里插入圖片描述
其中computeLaunchingTaskFlags(),是根據activity的launcher mode和intent.flag計算出activity的入堆疊方式,computeSourceStack()計算從哪個任務堆疊啟動該activity,

這個方法一路會呼叫到

RootActivityContainer.resumeFocusedStacksTopActivities()方法:


boolean resumeFocusedStacksTopActivities(
        ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
    ...
    //如果秒表堆疊就是堆疊頂Activity,啟動resumeTopActivityUncheckedLocked()
    if (targetStack != null && (targetStack.isTopStackOnDisplay()
        || getTopDisplayFocusedStack() == targetStack)) {
    result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
    ...
    if (!resumedOnDisplay) {
        // 獲取堆疊頂的ActivityRecord
        final ActivityStack focusedStack = display.getFocusedStack();
        if (focusedStack != null) {
            //最終呼叫startSpecificActivityLocked()
            focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
    }
  }
}

中間還會走到ActivityStack中呼叫方法,如:
在這里插入圖片描述
由于此時launcher行程沒有創建完成,所以走到else中,然后呼叫到
在這里插入圖片描述

接著看ActivityStackSupervisor.startSpecificActivityLocked()


void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
    ...
    //最終呼叫到AMS的startProcess()
    final Message msg = PooledLambda.obtainMessage(
    //這里的::是java8中的一個關鍵字,主要解決java8之前方法不能作為其它方法引數的問題
            ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
            r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
    mService.mH.sendMessage(msg);
    ...
}

ActivityManagerService.startProcess()方法:開始創建行程
在這里插入圖片描述
然后呼叫startProcessLocked方法,
在這里插入圖片描述
在startProcessLocked中,又進一步把行程創建作業委派給了ProcessList,這是Android10中對FrameWork進行改造而引入的,在老的版本中對行程的創建是在AMS中去發起請求的,谷歌團隊認為AMS已經非常臃腫了(改造前2.8W行,改造后1.9W行),所以引入ProcessList專門進行行程的創建,

接著看ProcessList.startProcessLocked方法:
在這里插入圖片描述
主要作用是,在行程創建之前,會配置一些必要的引數,比如版本號之類的,
接著,這個方法中有一個非常重要的引數:
在這里插入圖片描述
entryPoint就是新行程的入口,

在創建行程的時候,在這里強制指定為android.app.ActivityThread.

所以,所有Android應用的行程入口是ActivityThread,并不是我們通常理解的application,

創建行程所需要的引數配置完成后,最侄訓走到ZygoteProcess
在這里插入圖片描述
此時還是處于SystemServer行程,這個類的目的是創建本地socket連接物件,并且連接遠在Zygote行程的socket服務,然后通過字符輸入流,把創建行程所需要的引數發送過去,行程創建完成后,會根據傳遞的新行程的入口類,由zygotInit反射執行,

創建行程的呼叫堆疊如下:
在這里插入圖片描述

3.2.2.第二階段:Zygote fork一個Launcher行程的階段

Zygote的啟動程序我們前面有簡單了解,SystemServer的AMS服務向啟動launcher發起一個fork請求,Zygote行程通過Linux的fork函式,范訓出一個新的行程,

由于Zygote行程在啟動時會創建Java虛擬機,因此通過fork而創建的Launcher程式行程可以在內部獲取一個Java虛擬機的實體拷貝,fork采用copy-on-write機制,有些類如果不做改變,甚至都不用復制,子行程可以和父行程共享這部分資料,從而省去不少記憶體的占用,

Zygote的呼叫堆疊如下:
在這里插入圖片描述
ZygoteInit.main():

public static void main(String argv[]) {
    ...
    Runnable caller;
    ....
    if (startSystemServer) {
        //Zygote Fork出的第一個行程 SystmeServer
        Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
 
        if (r != null) {
            r.run();
            return;
        }
    }
    ...
    //回圈等待fork出其他的應用行程,比如Launcher
    //最終通過呼叫processOneCommand()來進行行程的處理
    caller = zygoteServer.runSelectLoop(abiList);
    ...
    if (caller != null) {
        caller.run(); //執行回傳的Runnable物件,進入子行程
    }
}

Zygote先fork出SystemServer行程,接著進入回圈等待,用來接收Socket發來的訊息,用來fork出其他應用行程,比如Launcher,

ZygoteConnection.processOneCommand()


Runnable processOneCommand(ZygoteServer zygoteServer) {
    int pid = -1;
    ...
    //Fork子行程,得到一個新的pid
    /fork子行程,采用copy on write方式,這里執行一次,會回傳兩次
    ///pid=0 表示Zygote  fork子行程成功
    //pid > 0 表示子行程 的真正的PID
    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
            parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
            parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);
    ...
    if (pid == 0) {
        // in child, fork成功,第一次回傳的pid = 0
        ...
        return handleChildProc(parsedArgs, descriptors, childPipeFd,
                parsedArgs.mStartChildZygote);
    } else {
        //in parent
        ...
        childPipeFd = null;
        handleParentProc(pid, descriptors, serverPipeFd);
        return null;
    }
}

通過forkAndSpecialize()來fork出Launcher的子行程,并執行handleChildProc,進入子行程的處理.

ZygoteConnection.handleChildProc():

private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
        FileDescriptor pipeFd, boolean isZygote) {
    ...
    if (parsedArgs.mInvokeWith != null) {
        ...
        throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
    } else {
        if (!isZygote) {
            // App行程將會呼叫到這里,執行目標類的main()方法
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, null /* classLoader */);
        } else {
            return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, null /* classLoader */);
        }
    }
}

進行子行程的操作,最侄訓得需要執行的ActivityThread的main()

然后把之前傳來的"android.app.ActivityThread" 傳遞給findStaticMain:

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) {
    ...
    // startClass: 如果AMS通過socket傳遞過來的是 ActivityThread
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

通過反射,拿到ActivityThread的main()方法:

protected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {
    Class<?> cl;
 
    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }
 
    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException(
                "Problem getting static main on " + className, ex);
    }
 
    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException(
                "Main method is not public and static on " + className);
    }
    return new MethodAndArgsCaller(m, argv);
}

把反射得來的ActivityThread main()入口回傳給ZygoteInit的main,通過caller.run()進行呼叫:

static class MethodAndArgsCaller implements Runnable {
    /** method to call */
    private final Method mMethod;
 
    /** argument array */
    private final String[] mArgs;
 
    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }
 
    //呼叫ActivityThread的main()
    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            Throwable cause = ex.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw new RuntimeException(ex);
        }
    }
}

到這里,就執行到了ActivityThread的main()方法,

3.2.3.第三階段:進入ActivityThread的main(),完成最終Launcher的onCreate操作

Zygote fork出了Launcher的行程,并把接下來的Launcher啟動任務交給了ActivityThread來進行,接下來我們就從ActivityThread main()來分析Launcher的創建程序,
在這里插入圖片描述
ActivityThread.main():Java程式的入口,直接找到main方法,


public static void main(String[] args) {
 	 ...
  	//初始化looper
    Looper.prepareMainLooper();
  	...
    ActivityThread thread = new ActivityThread();
    //建立Binder通道 (創建新執行緒)
    thread.attach(false, startSeq);
  	...
  	//主執行緒開始輪訓,如果退出,說明程式關閉
    Looper.loop();
}

正式因為在行程的入口方法里面開始了訊息佇列的輪訓,所以主執行緒才有了訊息分發的機制,

ActivityThread的attach會一直呼叫到AMS的attachApplication方法,

ActivityManagerService.attachApplication()


public final void attachApplication(IApplicationThread thread, long startSeq) {
    synchronized (this) {
    	//通過Binder獲取傳入的pid資訊
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        Binder.restoreCallingIdentity(origId);
        ...
        ...
    }
}

在這個方法中,又呼叫一系列方法,這里省略大量代碼,最終主要做了兩件事:
1.調度ActivityThread創建Application,并呼叫了application的onCreate()方法,如圖:
在這里插入圖片描述

2.繼續啟動行程創建之前已經加入任務堆疊的那個Activity,也就是Launcher應用的第一個Activity,
在這里插入圖片描述mAtmIntetnal.attachApplication(…),這里的mAtmIntetnal是ActivityTaskManagerInternal,這個方法的作用就是啟動launcher應用的第一個Activity,根據前面講的閱讀原始碼技巧,這里可以找到該方法的實作在ActivityTaskManagerService中:
在這里插入圖片描述
這里進一步交給了RootActivityContainer,接著看,這里很關鍵:
在這里插入圖片描述
終于層層呼叫到ActivityStackSupervisor.java的 **realStartActivityLocked()**進行Activity的啟動了,

ActivityStackSupervisor.realStartActivityLocked():


boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {
  try {
            //創建一個啟動Activity的事務物件
            final ClientTransaction clientTransaction = ClientTransaction.obtain(
                    proc.getThread(), r.appToken);
      		//往事務里面添加一個任務,叫LaunchActivityItem
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                    System.identityHashCode(r), r.info,
                    // TODO: Have this take the merged configuration instead of separate global
                    // and override configs.
                    mergedConfiguration.getGlobalConfiguration(),
                    mergedConfiguration.getOverrideConfiguration(), r.compat,
                    r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                    r.icicle, r.persistentState, results, newIntents,
                    dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                            r.assistToken));
 
      ...
            final ActivityLifecycleItem lifecycleItem;
            //這里就是上面提到的andResume引數,只有當該Activity在堆疊頂才為True.
            if (andResume) {
            //執行Activity的resume生命周期方法
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
            } else {
             //執行Activity的pause生命周期方法
                lifecycleItem = PauseActivityItem.obtain();
            }
            clientTransaction.setLifecycleStateRequest(lifecycleItem);
		 	//開始事務的執行
 			mService.getLifecycleManager().scheduleTransaction(clientTransaction);
  ...

}

這里的LaunchActivityItem,ResumeActivityItem等也是在Android10.0的改動,把Activity的生命周期拆分成ActivityLifecycleItem,根據不同的狀態讓不同的ActivityLifecycleItem去執行不同的activity的生命周期,這種設計模式叫做狀態機
在這里插入圖片描述
在狀態機模式中,將每一個條件的分支,放入一個獨立的類中,去除過多的 if else 分支陳述句,

接著看上面,一旦執行了scheduleTransaction開啟事務,就會呼叫到LaunchActivityItem的excute方法,然后會呼叫到ClientTranscationHandler的handlerLaunchActivity方法,
在這里插入圖片描述
我們知道ClientTranscationHandler是ActivityThread的父類,是一個抽象類,所以直接找到ActivityThread中去查看,在ActivityThread的handlerLaunchActivity中又會呼叫到performLaunchActivity方法:

在這里插入圖片描述
這個方法就會去真正的啟動Activity,并且回傳一個Activity物件,當得到這個Activity的實體物件之后,接著看該方法下面的代碼:

在這里插入圖片描述
跟進這個方法:


public void callActivityOnCreate(Activity activity, Bundle icicle,
        PersistableBundle persistentState) {
    prePerformCreate(activity); //activity onCreate的預處理
    activity.performCreate(icicle, persistentState);//執行onCreate()
    postPerformCreate(activity); //activity onCreate創建后的一些資訊處理
}

performCreate()主要呼叫Activity的onCreate():


final void performCreate(Bundle icicle, PersistableBundle persistentState) {
  ...
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
        onCreate(icicle);
    }
  ...
}
同理:如果還往事務中添加了ResumeActivityItem,相應的也會執行到Activity的onResume生命周期,

好了,到這里終于看到我們熟悉的Activity的onCreate(),到這里Launcher應用才啟動完成,Launcher被真正創建起來,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/262075.html

標籤:其他

上一篇:實作聯系人功能,右側A~Z滑動

下一篇:抱著試一試的心態,沒想到還真被錄用了!年后我收到的第一個offer:位元組跳動Android研發崗

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more