Android Framework - 學(xué)習(xí)起步

前言

作為一名合格的 Android 開發(fā),需要學(xué)習(xí) Framework 知識,來解決 App 穩(wěn)定性相關(guān)的問題

Framework 的源碼學(xué)習(xí)一般由 init.rc 開始看起,因為它是一個 Android 系統(tǒng)啟動必備的重要腳本,之后的幾大系統(tǒng)進(jìn)程都是由它啟動的,比如 zygote,systemserver 等,這里主要記錄一些基本概念,以及 Zygote 啟動的源碼分析

Binder 原理是相對較難的一個部分,先看其他系統(tǒng)源碼,等功力足夠時再拜讀

init.rc

init 啟動的四個重要進(jìn)程如下:

  1. Zygote 創(chuàng)建 App 的進(jìn)程
  2. ServiceManager 負(fù)責(zé)c/s通信管理的進(jìn)程
  3. SurfaceFlingler 顯示渲染服務(wù)
  4. Media 多媒體服務(wù)

SystemServer

Zygote 進(jìn)程啟動時,會順帶啟動 SystemServer 進(jìn)程

fork 是通過 native 方法調(diào)用,返回 pid 給到 Java 層

PMS

負(fù)責(zé)安裝卸載 app收班,主要用于解析 apk 文件等操作

fork 進(jìn)程的理解

我們都知道新開啟的 App 進(jìn)程都是由 Zygote.fork 出來的祝旷,那 fork 到底是個什么操作呢

Zygote進(jìn)程和app進(jìn)程fork過程分析

首先 fork 是叉子的意思,我們所提及的 fork 實際為 native 層的 fork() 函數(shù),當(dāng)執(zhí)行該函數(shù)時,會對父進(jìn)程進(jìn)行一次拷貝,拷貝完成過后,但用戶內(nèi)存空間是彼此獨(dú)立,從此刻開始,開始各走各的

fork() 調(diào)用一次,返回兩次,這是特性

fork的原理及實現(xiàn)

  • AMS 和 Zygote 間是怎樣通信的

AMS 通過 Socket 來通知 Zygote 進(jìn)程坏挠,發(fā)送過程是怎樣的换况,可以看下這篇文章 fork 進(jìn)程的過程

AMS -> Zygote 通過 socket 請求缔御,Zygote 調(diào)用 fork() 方法朝氓,成功后返回給 AMS径荔,新進(jìn)程創(chuàng)建后調(diào)用 AtivityThread.main()

對于 epoll 的理解

全名是 EventPoll 垂寥,poll 是輪詢的意思颠黎,是一個事件通知機(jī)制另锋,性能上比較好,Handler 底層的事件喚醒也是用這玩意盏缤,具體是怎么實現(xiàn)的砰蠢,不太清楚,先挖個坑,后面找個時間拜讀一下源碼

Zygote 啟動過程

首先 Android 系統(tǒng)的第一個進(jìn)程為 init 進(jìn)程,負(fù)責(zé)解析執(zhí)行 init.rc ,并且啟動了 Zygote 進(jìn)程

接下來通過閱讀源碼,分析一下 Zygote 的啟動過程,從 AndroidRuntime,cpp 的 start() 開始看起

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    const char* rootDir = getenv("ANDROID_ROOT")
    // 創(chuàng)建虛擬機(jī)
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);
     // 傳入 ZygoteInit 的類路徑
    // 當(dāng)前線程為虛擬機(jī)的主線程
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    //執(zhí)行到這里虛擬機(jī)退出,應(yīng)該對應(yīng)著進(jìn)程銷毀
    free(slashClassName);
    ALOGD("Shutting down VM\n");
}

通過 native 創(chuàng)建了虛擬機(jī)實例,然后通過 JNI 調(diào)用 ZygoteInit ,開始到 Java 層的邏輯了

  • Java 層
// ZygoteInit.class
  public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;
        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ZygoteHooks.startZygoteNoThreadCreation();
        // Zygote goes into its own process group.
        try {
            Os.setpgid(0, 0);
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }
        Runnable caller;
        try {
            // 開啟 DDMMS 
            RuntimeInit.preForkInit();
            boolean startSystemServer = false;
            String zygoteSocketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            // 判斷各種配置開關(guān)
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }
            // 判斷 SocketName 是否為 "zygote"
            final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
            if (!isRuntimeRestarted) {
                if (isPrimaryZygote) {
                    FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
                            BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
                            startTime);
                } 
                
                // 判斷 SocketName 是否為 "zygote_secondary"
                else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
                    FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
                            BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
                            startTime);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }

            //是否預(yù)加載(具體為預(yù)加載 class / 類加載器 / 資源文件等)
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                        SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            }

            // Do an initial gc to clean up after startup
            bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
            // 觸發(fā)一次 gc (具體由 ZygoteHooks執(zhí)行)
            gcAndFinalize();
            bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC

            bootTimingsTraceLog.traceEnd(); // ZygoteInit

            Zygote.initNativeState(isPrimaryZygote);

            ZygoteHooks.stopZygoteNoThreadCreation();
            // ZygoteServer 做為 Socket 服務(wù)端,創(chuàng)建 socket 連接
            zygoteServer = new ZygoteServer(isPrimaryZygote);

            // 創(chuàng)建 SystemSerrver 進(jìn)程,通過 Zygote fork 出來,之后 SystemSerrver 負(fù)責(zé)創(chuàng)建 AMS 等重要服務(wù)
            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }

            Log.i(TAG, "Accepting command socket connections");

            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.
            // 輪詢執(zhí)行,先不關(guān)注這塊
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket();
            }
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if (caller != null) {
            caller.run();
        }
    }


總結(jié)一下 Zygote 的啟動過程:

  1. 解析 init.rc 創(chuàng)建 AppRunTime
  2. 執(zhí)行 AndroidRuntime.Start()
  3. JNI 調(diào)用 ZygoteInit.main(),轉(zhuǎn)到 Java 層了
  4. 創(chuàng)建 Socket 服務(wù)端,準(zhǔn)備相應(yīng)客戶端的請求
  5. 預(yù)加載
  6. 通過 fork 的方式啟動 SystemServer 進(jìn)程
  7. 開啟輪詢

相關(guān)鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唉铜,一起剝皮案震驚了整個濱河市台舱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌潭流,老刑警劉巖竞惋,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灰嫉,居然都是意外死亡拆宛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門讼撒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浑厚,“玉大人,你說我怎么就攤上這事根盒∏” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵炎滞,是天一觀的道長敢艰。 經(jīng)常有香客問我,道長册赛,這世上最難降的妖魔是什么钠导? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮森瘪,結(jié)果婚禮上牡属,老公的妹妹穿的比我還像新娘。我一直安慰自己扼睬,他們只是感情好湃望,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痰驱,像睡著了一般证芭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上担映,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天废士,我揣著相機(jī)與錄音,去河邊找鬼蝇完。 笑死官硝,一個胖子當(dāng)著我的面吹牛矗蕊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氢架,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼傻咖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了岖研?” 一聲冷哼從身側(cè)響起卿操,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孙援,沒想到半個月后害淤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拓售,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年窥摄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片础淤。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡崭放,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸽凶,到底是詐尸還是另有隱情币砂,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布吱瘩,位于F島的核電站,受9級特大地震影響迹缀,放射性物質(zhì)發(fā)生泄漏使碾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一祝懂、第九天 我趴在偏房一處隱蔽的房頂上張望票摇。 院中可真熱鬧,春花似錦砚蓬、人聲如沸矢门。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟剔。三九已至,卻和暖如春摩梧,著一層夾襖步出監(jiān)牢的瞬間物延,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工仅父, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叛薯,地道東北人浑吟。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像耗溜,于是被迫代替她去往敵國和親组力。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

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