Android進(jìn)階(三):Application啟動過程(最詳細(xì)&最簡單)

1.前言

  • 最近一直在看 《Android進(jìn)階解密》 的一本書,這本書編寫邏輯、流程都非常好拳亿,而且很容易看懂,非常推薦大家去看看(沒有收廣告費愿伴,單純覺得作者寫的很好)肺魁。
  • 上一篇簡單的介紹了Android進(jìn)階(二): 應(yīng)用進(jìn)程啟動過程,最終知道了ActivityThread就是代表應(yīng)用進(jìn)程隔节。
  • 今天就介紹ActivityThread啟動之后鹅经,是如何啟動 Application (基于Android 8.0 系統(tǒng))。
  • 文章中實例 linhaojian的Github

2.Application啟動過程的時序圖

Application啟動流程.png

3.源碼分析

3.1 ActivityThread初始化

public static void main(String[] args) {
        //...
        //創(chuàng)建ActivityThread對象 & 調(diào)用attach()
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        //創(chuàng)建主線程的Handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //創(chuàng)建主線程的Looper
        Looper.loop(); 
}
  • 主要做了2件事怎诫,實例化ActivityThread & 創(chuàng)建主線程Handler與Looper接收信息瘾晃;

3.2 ActivityThread的attach函數(shù)

    private void attach(boolean system, long startSeq) {
        //...
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            // 獲取ActivityManagerService的代理對象
            final IActivityManager mgr = ActivityManager.getService();// 1
            try {
                //通知AMS進(jìn)行application的初始化
                mgr.attachApplication(mAppThread, startSeq);// 2
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
    }
  • 注釋1:獲取ActivityManagerService的代理對象
  • 注釋2:通過代理對象調(diào)用attachApplication()幻妓,獲取啟動application所需信息(應(yīng)用進(jìn)程相關(guān)數(shù)據(jù))蹦误;

3.3 ActivityManagerService的attachApplication函數(shù)

    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);// 1
            Binder.restoreCallingIdentity(origId);
        }
    }
  • 注釋1:調(diào)用attachApplicationLocked()

3.4 ActivityManagerService的attachApplicationLocked函數(shù)

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        // 根據(jù)pid獲取存儲在AMS中肉津,對應(yīng)進(jìn)程的相關(guān)信息
        ProcessRecord app;
        long startTime = SystemClock.uptimeMillis();
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);// 1
            }
        } else {
            app = null;
        }
        // ...
        thread.bindApplication(processName, appInfo, providers, null, profilerInfo, // 2
            null, null, null, testMode,
            mBinderTransactionTrackingEnabled, enableTrackAllocation,
            isRestrictedBackupMode || !normalMode, app.persistent,
            new Configuration(getGlobalConfiguration()), app.compat,
            getCommonServicesLocked(app.isolated),
            mCoreSettingsObserver.getCoreSettingsLocked(),
            buildSerial, isAutofillCompatEnabled);
         // ...
    }
  • 注釋1:根據(jù)pid獲取存儲在AMS中强胰,對應(yīng)應(yīng)用進(jìn)程的相關(guān)信息
  • 注釋2:通知ActivityThread啟動application(IApplicationThread是ActivityThread的內(nèi)部類阀圾,負(fù)責(zé)與ActivityManagerService通訊)哪廓;

3.5 ActivityThread的handleBindApplication函數(shù)

  • AMS中調(diào)用了ActivityThread的bindApplication函數(shù)狗唉,其內(nèi)部其實是完成了Handler切換到主線程初烘,并且最后活調(diào)用handleBindApplication(),下面我們看看其內(nèi)部源碼;
    private void handleBindApplication(AppBindData data) {
        // 將UI線程注冊為運行時的虛擬機(jī).
        VMRuntime.registerSensitiveThread();
        if (data.trackAllocation) {
            DdmVmInternal.enableRecentAllocations(true);
        }
        // ...
        // 創(chuàng)建上下文對象
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        updateLocaleListFromAppContext(appContext,
                mResourcesManager.getConfiguration().getLocales());
        if (!Process.isIsolated()) {
            final int oldMask = StrictMode.allowThreadDiskWritesMask();
            try {
                setupGraphicsSupport(appContext);
            } finally {
                StrictMode.setThreadPolicyMask(oldMask);
            }
        } else {
            ThreadedRenderer.setIsolatedProcess(true);
        }
        // Continue loading instrumentation.
        if (ii != null) {
            // ...
        } else {
            mInstrumentation = new Instrumentation();// 1
            mInstrumentation.basicInit(this);
        }
        Application app;
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            // 初始化Applcation類
            app = data.info.makeApplication(data.restrictedBackupMode, null); // 2
            // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
            mInitialApplication = app;
            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }
            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs); // 3
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            try {
                //調(diào)用Applcation的OnCreate函數(shù)
                mInstrumentation.callApplicationOnCreate(app); // 4
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
        } finally {
            // If the app targets < O-MR1, or doesn't change the thread policy
            // during startup, clobber the policy to maintain behavior of b/36951662
            if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                    || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }
        // Preload fonts resources
        FontsContract.setApplicationContextForResources(appContext);
        if (!Process.isIsolated()) {
            try {
                final ApplicationInfo info =
                        getPackageManager().getApplicationInfo(
                                data.appInfo.packageName,
                                PackageManager.GET_META_DATA /*flags*/,
                                UserHandle.myUserId());
                if (info.metaData != null) {
                    final int preloadedFontsResource = info.metaData.getInt(
                            ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
                    if (preloadedFontsResource != 0) {
                        data.info.getResources().preloadFonts(preloadedFontsResource);
                    }
                }
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }
  • 注釋1:創(chuàng)建Instrumentation,負(fù)責(zé)跟蹤Application還在Activity的生命周期分俯;
  • 注釋2:創(chuàng)建Application對象 & 調(diào)用其attach()肾筐;
  • 注釋3:調(diào)用Instrumentation的onCreate(),內(nèi)部是空實現(xiàn)缸剪;
  • 注釋4:調(diào)用Instrumentation的callApplicationOnCreate()吗铐,內(nèi)部是調(diào)用application的onCreate,如下代碼杏节;
public class Instrumentation {
    public void callApplicationOnCreate(Application app) {
        app.onCreate();
    }
}

4.類關(guān)系

Application啟動類結(jié)構(gòu).png
  • ActivityThread :通過IActivityManager類唬渗,通知AMS準(zhǔn)備application啟動所需進(jìn)程數(shù)據(jù) ;
  • ActivityManagerService :獲取application啟動所需進(jìn)程數(shù)據(jù) 奋渔;
  • Instrumentation :創(chuàng)建&啟動Application镊逝;跟蹤Application的生命周期;

5.總結(jié)

  • 到此嫉鲸,Application啟動過程介紹完畢撑蒜。
  • 如果喜歡我的分享,可以點擊 關(guān)注 或者 ,你們支持是我分享的最大動力 座菠。
  • linhaojian的Github

歡迎關(guān)注linhaojian_CSDN博客或者linhaojian_簡書狸眼!

不定期分享關(guān)于安卓開發(fā)的干貨。


寫技術(shù)文章初心

  • 技術(shù)知識積累
  • 技術(shù)知識鞏固
  • 技術(shù)知識分享
  • 技術(shù)知識交流
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浴滴,一起剝皮案震驚了整個濱河市拓萌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌升略,老刑警劉巖司志,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異降宅,居然都是意外死亡骂远,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門腰根,熙熙樓的掌柜王于貴愁眉苦臉地迎上來激才,“玉大人,你說我怎么就攤上這事额嘿∪衬眨” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵册养,是天一觀的道長东帅。 經(jīng)常有香客問我,道長球拦,這世上最難降的妖魔是什么靠闭? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮坎炼,結(jié)果婚禮上愧膀,老公的妹妹穿的比我還像新娘。我一直安慰自己谣光,他們只是感情好檩淋,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著萄金,像睡著了一般蟀悦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上氧敢,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天日戈,我揣著相機(jī)與錄音,去河邊找鬼福稳。 笑死涎拉,一個胖子當(dāng)著我的面吹牛瑞侮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼓拧,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼半火,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了季俩?” 一聲冷哼從身側(cè)響起钮糖,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酌住,沒想到半個月后店归,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡酪我,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年消痛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片都哭。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秩伞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出欺矫,到底是詐尸還是另有隱情纱新,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布穆趴,位于F島的核電站脸爱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏未妹。R本人自食惡果不足惜簿废,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望教寂。 院中可真熱鬧捏鱼,春花似錦、人聲如沸酪耕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迂烁。三九已至,卻和暖如春递鹉,著一層夾襖步出監(jiān)牢的瞬間盟步,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工躏结, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留却盘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像黄橘,于是被迫代替她去往敵國和親兆览。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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