視頻錄制

本篇來自 **WizardDragon **的投稿餐禁,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題屹培。文章篇幅不短,希望能對大家有所幫助怔檩。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext褪秀、onCreate、call 等)啟動順序薛训,跟我們之前理解的稍稍不一樣媒吗。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決乙埃。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext闸英、onCreate、call 等)被系統(tǒng)調(diào)用的順序介袜,我們創(chuàng)建一個簡單的應用甫何,只包含這5個組件,不考慮一個應用多進程的情況遇伞,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中辙喂。。。(3)]
MainService.java
[圖片上傳中加派。叫确。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時芍锦,均已冷啟動的方式啟動應用竹勉。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk娄琉。
注意在測試的手機上次乓,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一寥茫,點擊桌面的圖標啟動應用望艺,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用封救,其中啟動 MainService 的代碼如下:

日志如下:
[圖片上傳中女气。杏慰。。(9)]
場景三炼鞠,應用通過接受開機廣播啟動的方式啟動缘滥,日志如下:
[圖片上傳中。谒主。朝扼。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動霎肯,其中擎颖,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。观游。搂捧。(11)]
日志如下:
[圖片上傳中。备典。异旧。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行提佣;
**3. **Activity吮蛹、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的拌屏;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity潮针、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎倚喂?
為了驗證這個問題每篷,MainApplication 的代碼不變瓣戚,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。焦读。子库。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中矗晃。仑嗅。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后张症,才會執(zhí)行 Application 的 onCreate 的仓技。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題俗他,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中脖捻。。兆衅。(15)]
[圖片上傳中地沮。。涯保。(16)]
我們還在第四個場景下驗證诉濒,日志如下:
[圖片上傳中周伦。夕春。。(17)]
從日志中可以發(fā)現(xiàn)专挪,Application 的 onCreate 執(zhí)行時及志,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行寨腔,而是會同時執(zhí)行**速侈。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有,比如:Application所在類的構造方法。為了驗證這個問題兔簇,將代碼改為:
[圖片上傳中险毁。。指攒。(18)]
程序啟動后,日志為:
[圖片上傳中。眨层。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用上荡。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢趴樱?有,自己可以再想想哦。

遇到的坑

好了叁征,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“纳账,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)捺疼,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中塞祈。嗯,一切感覺起來那么美好帅涂,直到你運行程序崩潰時...
好吧好吧议薪,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中媳友,使用了 getApplicationContext方法斯议。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰醇锚。
所以哼御,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧焊唬,別再使用 getApplicationContext() 方法了恋昼。下文有分析為什么。
“坑”二:這個其實不算很坑赶促,也不會引起崩潰液肌,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider鸥滨,那么這個 ContentProvider 會被初始化兩次嗦哆,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯婿滓,那么一定要檢查是否會有影響老速。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法凸主,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法橘券,Application 的代碼,Provider 的代碼卿吐,Activity 的代碼分別如下:
[圖片上傳中旁舰。。但两。(20)]
啟動應用后鬓梅,日志如下:
[圖片上傳中。谨湘。绽快。(21)]
可以看到芥丧,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類坊罢,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個续担。

源碼分析

好了,現(xiàn)象活孩、問題和“坑”都經(jīng)歷了一遍物遇,那么 為什么 會是這樣呢?
我們通過看源碼憾儒,來跟蹤:
1. Application 的 attachBaseContext杨箭、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序唆貌。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null猎塞。
先看第一個問題隅肥,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建训裆,已經(jīng)幫我們進行封裝眶根,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼边琉,本文以下的源碼都以
6.0.1_r10
基礎属百。
a. ActivityThread.java 的 main方法:
[圖片上傳中。变姨。族扰。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。钳恕。别伏。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。忧额。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中愧口。睦番。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中耍属。托嚣。。(26)]
f. Application.java 的 attach方法
[圖片上傳中厚骗。示启。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中领舰。夫嗓。迟螺。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的舍咖,還是在 ActivityThread.java 中:
[圖片上傳中矩父。。排霉。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中窍株。。攻柠。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中球订。。瑰钮。(31)]
看第二問題辙售,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢飞涂?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中旦部。。较店。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中士八。。梁呈。(33)]
3. mPackageInfo 是什么時候賦值的呢婚度?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼官卡,跟進代碼發(fā)現(xiàn)蝗茁,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中寻咒。哮翘。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)毛秘,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時饲嗽,就到了注釋b氮昧,而此時mPackageInfo是不為空的宪郊,所以會執(zhí)行mPackageInfo.getApplication()昼激,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)抹恳,
[圖片上傳中员凝。。奋献。(35)]
[圖片上傳中健霹。旺上。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時骤公, mApplication 還沒有被賦值抚官,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后阶捆,mApplication 才會被賦值凌节。
附圖一張:
[圖片上傳中。洒试。倍奢。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果垒棋,并且深入源碼去分析遇到的問題卒煞。文章篇幅不短,希望能對大家有所幫助叼架。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時畔裕,我們想在應用最早啟動時,先做一些判斷乖订,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務扮饶。
對其他應用提供服務指的是,我們的應用中有 ContentProvider乍构,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider甜无,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時哥遮,我們的應用存在啟動和未啟動的兩種情況岂丘。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中眠饮,但等到測試時發(fā)現(xiàn)了很多意想不到的情況奥帘,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務君仆,而有時候第三方應用不能使等等的問題翩概。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext返咱、onCreate、call 等)啟動順序牍鞠,跟我們之前理解的稍稍不一樣咖摹。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決难述。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext吐句、onCreate、call 等)被系統(tǒng)調(diào)用的順序店读,我們創(chuàng)建一個簡單的應用嗦枢,只包含這5個組件,不考慮一個應用多進程的情況屯断,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中文虏。。殖演。(3)]
MainService.java
[圖片上傳中氧秘。。趴久。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時嘱丢,均已冷啟動的方式啟動應用掂为。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上目木,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用烤芦,日志如下:

場景二劈猿,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中坎吻。缆蝉。。(8)]
日志如下:

場景三瘦真,應用通過接受開機廣播啟動的方式啟動刊头,日志如下:
[圖片上傳中。诸尽。原杂。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動您机,其中穿肄,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。际看。咸产。(11)]
日志如下:
[圖片上傳中。仲闽。脑溢。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行赖欣;
**3. **Activity屑彻、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法验庙,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity社牲、Service 等的 onCreate(Activity 和 Service 不分先后)粪薛;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題搏恤,MainApplication 的代碼不變违寿,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。挑社。陨界。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中痛阻。菌瘪。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后阱当,才會執(zhí)行 Application 的 onCreate 的俏扩。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題弊添,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中录淡。蛋辈。苗胀。(15)]
[圖片上傳中。瓦堵。基协。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中谷丸。堡掏。。(17)]
從日志中可以發(fā)現(xiàn)刨疼,Application 的 onCreate 執(zhí)行時泉唁,ContentProvider 的 call方法 也在同時執(zhí)行倡蝙。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行躏哩,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎锣枝?
有迎卤,比如:Application所在類的構造方法拴鸵。為了驗證這個問題,將代碼改為:
[圖片上傳中。劲藐。八堡。(18)]
程序啟動后,日志為:
[圖片上傳中聘芜。兄渺。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用汰现。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有妄田,自己可以再想想哦晓褪。

遇到的坑

好了塘秦,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“鸭轮,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)沥曹,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中施符。嗯,一切感覺起來那么美好屁置,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中溉奕,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰签杈。
所以,如果在 attachBaseContext方法 中要使用 context 的話擦秽,那么使用 **this **吧缅茉,別再使用 getApplicationContext() 方法了帆离。下文有分析為什么。
“坑”二:這個其實不算很坑,也不會引起崩潰岸夯,但需要注意:
在 Application 的 attachBaseContext方法 中麻献,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次猜扮,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate勉吻。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響旅赢。
做一下驗證柬脸,在 Application 中調(diào)用 Provider 的 call方法等限,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法爸吮,Application 的代碼,Provider 的代碼望门,Activity 的代碼分別如下:
[圖片上傳中形娇。。筹误。(20)]
啟動應用后桐早,日志如下:
[圖片上傳中。纫事。勘畔。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)丽惶,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類炫七,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了钾唬,現(xiàn)象万哪、問題和“坑”都經(jīng)歷了一遍夭拌,那么 為什么 會是這樣呢蒂胞?
我們通過看源碼莲绰,來跟蹤:
1. Application 的 attachBaseContext撤卢、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序核行。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null钦奋。
先看第一個問題再扭,我們知道Java進程的入口方法一般都是在main中骗污,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建着撩,已經(jīng)幫我們進行封裝诅福,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼拖叙,本文以下的源碼都以
6.0.1_r10
基礎氓润。
a. ActivityThread.java 的 main方法:
[圖片上傳中。薯鳍。咖气。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。。崩溪。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中浅役。。悯舟。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中担租。。抵怎。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中奋救。。反惕。(26)]
f. Application.java 的 attach方法
[圖片上傳中尝艘。。姿染。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中背亥。。悬赏。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法狡汉,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中闽颇。盾戴。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中兵多。尖啡。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中剩膘。衅斩。。(31)]
看第二問題怠褐,為什么在我們自定義 Application 中的 attachBaseContext方法 中畏梆,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中奈懒。具温。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中筐赔。。揖铜。(33)]
3. mPackageInfo 是什么時候賦值的呢茴丰?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)贿肩,果不其然峦椰,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。商蕴。椎镣。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)宣肚,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b滔金,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()茂嗓,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說餐茵,mPackageInfo是LoadedApk的實例),
[圖片上傳中述吸。忿族。。(35)]
[圖片上傳中蝌矛。道批。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時入撒, mApplication 還沒有被賦值隆豹,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后衅金,mApplication 才會被賦值噪伊。
附圖一張:
[圖片上傳中。氮唯。鉴吹。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果惩琉,并且深入源碼去分析遇到的問題豆励。文章篇幅不短,希望能對大家有所幫助瞒渠。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時良蒸,我們想在應用最早啟動時,先做一些判斷伍玖,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務嫩痰。
對其他應用提供服務指的是,我們的應用中有 ContentProvider窍箍,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider串纺,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果丽旅。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況纺棺。
剛開始榄笙,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況祷蝌,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”茅撞,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題巨朦。
于是我們跟蹤代碼米丘,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate罪郊、call 等)啟動順序蠕蚜,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后悔橄,我們才把遇到的問題徹底解決靶累。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate癣疟、call 等)被系統(tǒng)調(diào)用的順序挣柬,我們創(chuàng)建一個簡單的應用,只包含這5個組件睛挚,不考慮一個應用多進程的情況邪蛔,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。扎狱。侧到。(3)]
MainService.java
[圖片上傳中汽绢。般卑。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時紊遵,均已冷啟動的方式啟動應用污抬。
冷啟動汞贸,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上印机,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一矢腻,點擊桌面的圖標啟動應用,日志如下:

場景二射赛,通過另外一個應用以啟動Service的形式啟動應用多柑,其中啟動 MainService 的代碼如下:
[圖片上傳中。楣责。竣灌。(8)]
日志如下:
[圖片上傳中诫隅。。帐偎。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動蛔屹,日志如下:
[圖片上傳中削樊。。兔毒。(10)]
場景四漫贞,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中育叁,調(diào)用 MainProvider 的 call 代碼如下:

日志如下:
[圖片上傳中迅脐。。豪嗽。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的谴蔑;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity龟梦、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法隐锭,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity计贰、Service 等的 onCreate(Activity 和 Service 不分先后)钦睡;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題躁倒,MainApplication 的代碼不變荞怒,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。秧秉。褐桌。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中福贞。撩嚼。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后挖帘,才會執(zhí)行 Application 的 onCreate 的完丽。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題拇舀,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中逻族。。骄崩。(15)]
[圖片上傳中聘鳞。薄辅。。(16)]
我們還在第四個場景下驗證抠璃,日志如下:
[圖片上傳中站楚。。搏嗡。(17)]
從日志中可以發(fā)現(xiàn)窿春,Application 的 onCreate 執(zhí)行時震檩,ContentProvider 的 call方法 也在同時執(zhí)行箩绍。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行俐巴,而是會同時執(zhí)行**乙各。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎峰髓?
有膏萧,比如:Application所在類的構造方法冒窍。為了驗證這個問題懒叛,將代碼改為:
[圖片上傳中烦租。延赌。。(18)]
程序啟動后左权,日志為:
[圖片上傳中皮胡。。赏迟。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用屡贺。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有锌杀,自己可以再想想哦甩栈。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“糕再,那么在項目中為了能”盡早“的提前初始化某些模塊量没、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中突想。嗯殴蹄,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧猾担,那些“坑”終于還是來了袭灯。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法绑嘹。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時稽荧,內(nèi)心是崩潰。
所以工腋,如果在 attachBaseContext方法 中要使用 context 的話姨丈,那么使用 **this **吧畅卓,別再使用 getApplicationContext() 方法了。下文有分析為什么蟋恬。
“坑”二:這個其實不算很坑翁潘,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中歼争,去調(diào)用自身的 ContentProvider唐础,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate矾飞。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響呀邢。
做一下驗證洒沦,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法价淌,Application 的代碼申眼,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中蝉衣。括尸。。(20)]
啟動應用后病毡,日志如下:
[圖片上傳中濒翻。。啦膜。(21)]
可以看到有送,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類僧家,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了,現(xiàn)象棍掐、問題和“坑”都經(jīng)歷了一遍楔壤,那么 為什么 會是這樣呢?
我們通過看源碼肌稻,來跟蹤:
1. Application 的 attachBaseContext清蚀、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null灯萍。
先看第一個問題轧铁,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建旦棉,已經(jīng)幫我們進行封裝齿风,其實應用 main方法 是在 ActivityThread.java 中的药薯。
我們查看 ActivityThread.java 的源碼救斑,本文以下的源碼都以
6.0.1_r10
基礎泵额。
a. ActivityThread.java 的 main方法:
[圖片上傳中。携添。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中经窖。窖壕。诈闺。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。台丛。生音。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中悠砚。裹粤。敢辩。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。协怒。。(26)]
f. Application.java 的 attach方法
[圖片上傳中兽叮。睡榆。宿崭。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。才写。劳曹。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的琅摩,還是在 ActivityThread.java 中:
[圖片上傳中铁孵。。房资。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中蜕劝。。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中岖沛。暑始。。(31)]
看第二問題婴削,為什么在我們自定義 Application 中的 attachBaseContext方法 中廊镜,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中唉俗。嗤朴。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中虫溜。雹姊。。(33)]
3. mPackageInfo 是什么時候賦值的呢衡楞?我們從 ContextImpl 實例化的地方入手吱雏,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)瘾境,果不其然秉剑,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中祸轮。闸天。奶稠。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時盒犹,就到了注釋b懂更,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()急膀,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說沮协,mPackageInfo是LoadedApk的實例),
[圖片上傳中卓嫂。慷暂。。(35)]
[圖片上傳中晨雳。行瑞。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時餐禁, mApplication 還沒有被賦值血久,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后帮非,mApplication 才會被賦值氧吐。
附圖一張:
[圖片上傳中讹蘑。。筑舅。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿座慰,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題翠拣。文章篇幅不短版仔,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時误墓,我們想在應用最早啟動時蛮粮,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務优烧。
對其他應用提供服務指的是蝉揍,我們的應用中有 ContentProvider链峭,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider畦娄,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時弊仪,我們的應用存在啟動和未啟動的兩種情況熙卡。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中励饵,但等到測試時發(fā)現(xiàn)了很多意想不到的情況驳癌,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務役听,而有時候第三方應用不能使等等的問題颓鲜。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext典予、onCreate甜滨、call 等)啟動順序,跟我們之前理解的稍稍不一樣瘤袖。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后衣摩,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext捂敌、onCreate艾扮、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用占婉,只包含這5個組件泡嘴,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中逆济。委粉。。(3)]
MainService.java
[圖片上傳中认然。氧猬。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時阁将,均已冷啟動的方式啟動應用。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk击困。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一广凸,點擊桌面的圖標啟動應用阅茶,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用谅海,其中啟動 MainService 的代碼如下:
[圖片上傳中脸哀。。扭吁。(8)]
日志如下:
[圖片上傳中撞蜂。。侥袜。(9)]
場景三蝌诡,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中枫吧。浦旱。。(10)]
場景四九杂,其他應用調(diào)用 ContentProvider 的 call 方法啟動颁湖,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中例隆。甥捺。。(11)]
日志如下:
[圖片上傳中裳擎。涎永。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的鹿响;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行羡微;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法惶我,是在 MainApplication 的 onCreate 方法之后執(zhí)行的妈倔;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)绸贡;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎盯蝴?
為了驗證這個問題毅哗,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:


我們再在上面第四種場景上進行驗證捧挺,日志如下:
[圖片上傳中虑绵。。闽烙。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后翅睛,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎黑竞?
為了驗證這個問題捕发,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。很魂。扎酷。(15)]
[圖片上傳中。遏匆。法挨。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中拉岁。坷剧。惰爬。(17)]
從日志中可以發(fā)現(xiàn)喊暖,Application 的 onCreate 執(zhí)行時闯割,ContentProvider 的 call方法 也在同時執(zhí)行兽愤。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**肥缔。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎丛版?
有巩掺,比如:Application所在類的構造方法。為了驗證這個問題页畦,將代碼改為:
[圖片上傳中胖替。。豫缨。(18)]
程序啟動后独令,日志為:
[圖片上傳中。好芭。燃箭。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢舍败?有招狸,自己可以再想想哦敬拓。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“裙戏,那么在項目中為了能”盡早“的提前初始化某些模塊乘凸、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中累榜。嗯翰意,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧信柿,那些“坑”終于還是來了冀偶。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法渔嚷。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時进鸠,內(nèi)心是崩潰。
所以形病,如果在 attachBaseContext方法 中要使用 context 的話客年,那么使用 **this **吧,別再使用 getApplicationContext() 方法了漠吻。下文有分析為什么量瓜。
“坑”二:這個其實不算很坑,也不會引起崩潰途乃,但需要注意:
在 Application 的 attachBaseContext方法 中绍傲,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次耍共,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate烫饼。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響试读。
做一下驗證杠纵,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法钩骇,Application 的代碼比藻,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中倘屹。银亲。。(20)]
啟動應用后唐瀑,日志如下:
[圖片上傳中群凶。。哄辣。(21)]
可以看到请梢,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)赠尾,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個毅弧。

源碼分析

好了沫浆,現(xiàn)象辆毡、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢?
我們通過看源碼茧彤,來跟蹤:
1. Application 的 attachBaseContext屎勘、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序示启。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null湃密。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中庶香,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建甲棍,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的赶掖。
我們查看 ActivityThread.java 的源碼感猛,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中奢赂。陪白。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中膳灶。咱士。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中袖瞻。司致。拆吆。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中聋迎。。枣耀。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中霉晕。。捞奕。(26)]
f. Application.java 的 attach方法
[圖片上傳中牺堰。。颅围。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中伟葫。。院促。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法筏养,而這個方法是會調(diào)用到 installProvider方法 中的斧抱,還是在 ActivityThread.java 中:
[圖片上傳中。渐溶。辉浦。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。茎辐。宪郊。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。拖陆。弛槐。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中依啰,調(diào)用 getApplicationContext() 為 null 呢丐黄?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。孔飒。灌闺。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。坏瞄。桂对。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手鸠匀,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼蕉斜,跟進代碼發(fā)現(xiàn),果不其然缀棍,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中宅此。吨拗。式矫。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時柠逞,就到了注釋b青瀑,而此時mPackageInfo是不為空的璧亮,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說斥难,mPackageInfo是LoadedApk的實例)枝嘶,
[圖片上傳中。哑诊。群扶。(35)]
[圖片上傳中。。竞阐。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時提茁, mApplication 還沒有被賦值,所以返回的是空馁菜,只有把 attachBaseContext方法 執(zhí)行完成后茴扁,mApplication 才會被賦值。
附圖一張:
[圖片上傳中汪疮。峭火。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿智嚷,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果卖丸,并且深入源碼去分析遇到的問題。文章篇幅不短盏道,希望能對大家有所幫助稍浆。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時,我們想在應用最早啟動時猜嘱,先做一些判斷衅枫,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是朗伶,我們的應用中有 ContentProvider弦撩,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果论皆。當?shù)谌綉谜{(diào)用我們的應用時益楼,我們的應用存在啟動和未啟動的兩種情況。
剛開始点晴,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中感凤,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”粒督,但有時候第三方應用能夠使用服務陪竿,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼坠陈,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext萨惑、onCreate、call 等)啟動順序仇矾,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后解总,我們才把遇到的問題徹底解決贮匕。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate花枫、call 等)被系統(tǒng)調(diào)用的順序刻盐,我們創(chuàng)建一個簡單的應用掏膏,只包含這5個組件,不考慮一個應用多進程的情況敦锌,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中馒疹。。乙墙。(3)]
MainService.java
[圖片上傳中颖变。。膊升。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時讥蟆,均已冷啟動的方式啟動應用喻奥。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk衔峰。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一蛙粘,點擊桌面的圖標啟動應用垫卤,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用出牧,其中啟動 MainService 的代碼如下:
[圖片上傳中葫男。。崔列。(8)]
日志如下:
[圖片上傳中梢褐。。赵讯。(9)]
場景三盈咳,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中边翼。鱼响。。(10)]
場景四组底,其他應用調(diào)用 ContentProvider 的 call 方法啟動丈积,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中债鸡。江滨。。(11)]
日志如下:
[圖片上傳中厌均。唬滑。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行晶密;
**3. **Activity擒悬、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的稻艰;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity懂牧、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎尊勿?
為了驗證這個問題僧凤,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中运怖。拼弃。。(13)]
我們再在上面第四種場景上進行驗證摇展,日志如下:


問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后吻氧,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎咏连?
為了驗證這個問題盯孙,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。祟滴。振惰。(15)]
[圖片上傳中。垄懂。骑晶。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中草慧。桶蛔。。(17)]
從日志中可以發(fā)現(xiàn)漫谷,Application 的 onCreate 執(zhí)行時仔雷,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行舔示,而是會同時執(zhí)行**碟婆。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎现使?
有辜腺,比如:Application所在類的構造方法堤舒。為了驗證這個問題植锉,將代碼改為:
[圖片上傳中。的猛。柒巫。(18)]
程序啟動后刷袍,日志為:
[圖片上傳中锻煌。妓布。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用宋梧。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢匣沼?有,自己可以再想想哦捂龄。

遇到的坑

好了释涛,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊倦沧、功能或者參數(shù)唇撬,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中展融。嗯窖认,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧告希,那些“坑”終于還是來了扑浸。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法燕偶。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時喝噪,內(nèi)心是崩潰。
所以指么,如果在 attachBaseContext方法 中要使用 context 的話酝惧,那么使用 **this **吧,別再使用 getApplicationContext() 方法了伯诬。下文有分析為什么晚唇。
“坑”二:這個其實不算很坑,也不會引起崩潰姑廉,但需要注意:
在 Application 的 attachBaseContext方法 中缺亮,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次桥言,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate萌踱。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響号阿。
做一下驗證并鸵,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法扔涧,Application 的代碼园担,Provider 的代碼届谈,Activity 的代碼分別如下:
[圖片上傳中。弯汰。艰山。(20)]
啟動應用后,日志如下:
[圖片上傳中咏闪。曙搬。。(21)]
可以看到鸽嫂,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)纵装,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個据某。

源碼分析

好了癣籽,現(xiàn)象涣澡、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢乔妈?
我們通過看源碼蝙云,來跟蹤:
1. Application 的 attachBaseContext薪前、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序住拭。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題谒养,我們知道Java進程的入口方法一般都是在main中股淡,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建身隐,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的唯灵。
我們查看 ActivityThread.java 的源碼贾铝,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中早敬。忌傻。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中搞监。水孩。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中琐驴。俘种。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中绝淡。宙刘。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中牢酵。悬包。。(26)]
f. Application.java 的 attach方法
[圖片上傳中馍乙。布近。垫释。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。撑瞧。棵譬。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的预伺,還是在 ActivityThread.java 中:
[圖片上傳中订咸。。酬诀。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中脏嚷。。料滥。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中然眼。。葵腹。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中屿岂,調(diào)用 getApplicationContext() 為 null 呢践宴?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。爷怀。阻肩。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。运授。烤惊。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手吁朦,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼柒室,跟進代碼發(fā)現(xiàn),果不其然逗宜,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中雄右。。纺讲。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)擂仍,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b熬甚,而此時mPackageInfo是不為空的祟身,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說柄延,mPackageInfo是LoadedApk的實例),
[圖片上傳中冲簿。。亿昏。(35)]
[圖片上傳中。将硝。恭朗。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值依疼,所以返回的是空痰腮,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值律罢。
附圖一張:
[圖片上傳中膀值。。误辑。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿沧踏,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題巾钉。文章篇幅不短翘狱,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時睛琳,我們想在應用最早啟動時盒蟆,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務师骗。
對其他應用提供服務指的是历等,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider辟癌,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果寒屯。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況。
剛開始寡夹,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中处面,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”菩掏,但有時候第三方應用能夠使用服務魂角,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼智绸,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext野揪、onCreate、call 等)啟動順序瞧栗,跟我們之前理解的稍稍不一樣斯稳。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決迹恐。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext挣惰、onCreate、call 等)被系統(tǒng)調(diào)用的順序殴边,我們創(chuàng)建一個簡單的應用憎茂,只包含這5個組件,不考慮一個應用多進程的情況找都,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中唇辨。。能耻。(3)]
MainService.java
[圖片上傳中。亡驰。晓猛。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用凡辱。
冷啟動戒职,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上透乾,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一洪燥,點擊桌面的圖標啟動應用,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中类缤。登失。。(8)]
日志如下:
[圖片上傳中畏纲。。茵乱。(9)]
場景三芒篷,應用通過接受開機廣播啟動的方式啟動搜变,日志如下:
[圖片上傳中。针炉。挠他。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動篡帕,其中殖侵,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。赂苗。愉耙。(11)]
日志如下:
[圖片上傳中。拌滋。朴沿。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行败砂;
**3. **Activity赌渣、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的昌犹;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity坚芜、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎斜姥?
為了驗證這個問題鸿竖,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中铸敏。缚忧。。(13)]
我們再在上面第四種場景上進行驗證杈笔,日志如下:
[圖片上傳中闪水。。蒙具。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后球榆,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎禁筏?
為了驗證這個問題持钉,我們將 MainProvider 和 MainApplication 的代碼改為:


[圖片上傳中。融师。右钾。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中。舀射。窘茁。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時脆烟,ContentProvider 的 call方法 也在同時執(zhí)行山林。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**邢羔。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎驼抹?
有,比如:Application所在類的構造方法拜鹤。為了驗證這個問題框冀,將代碼改為:
[圖片上傳中。敏簿。明也。(18)]
程序啟動后,日志為:
[圖片上傳中惯裕。温数。彻秆。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用性穿。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有所宰,自己可以再想想哦握玛。

遇到的坑

好了够傍,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊挠铲、功能或者參數(shù)王带,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯市殷,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧刹衫,那些“坑”終于還是來了醋寝。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法带迟。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時音羞,內(nèi)心是崩潰。
所以仓犬,如果在 attachBaseContext方法 中要使用 context 的話嗅绰,那么使用 **this **吧,別再使用 getApplicationContext() 方法了。下文有分析為什么窘面。
“坑”二:這個其實不算很坑翠语,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中财边,去調(diào)用自身的 ContentProvider肌括,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate酣难。如果你在 ContentProvider 的 onCreate 中有一些邏輯谍夭,那么一定要檢查是否會有影響。
做一下驗證憨募,在 Application 中調(diào)用 Provider 的 call方法紧索,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼菜谣,Provider 的代碼珠漂,Activity 的代碼分別如下:
[圖片上傳中。葛菇。甘磨。(20)]
啟動應用后,日志如下:
[圖片上傳中眯停。济舆。。(21)]
可以看到莺债,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)滋觉,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個齐邦。

源碼分析

好了椎侠,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍措拇,那么 為什么 會是這樣呢我纪?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext丐吓、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序浅悉。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題券犁,我們知道Java進程的入口方法一般都是在main中术健,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝粘衬,其實應用 main方法 是在 ActivityThread.java 中的荞估。
我們查看 ActivityThread.java 的源碼咳促,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中勘伺。跪腹。凡伊。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中车猬。嘱吗。此迅。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中鲫趁。歪今。铣猩。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中怎虫。股毫。膳音。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。铃诬。祭陷。(26)]
f. Application.java 的 attach方法
[圖片上傳中。趣席。兵志。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。宣肚。想罕。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的霉涨,還是在 ActivityThread.java 中:
[圖片上傳中按价。。笙瑟。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中楼镐。。往枷。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中框产。。错洁。(31)]
看第二問題茅信,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢墓臭?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。妖谴。窿锉。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中酌摇。。嗡载。(33)]
3. mPackageInfo 是什么時候賦值的呢窑多?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼洼滚,跟進代碼發(fā)現(xiàn)埂息,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中遥巴。千康。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)铲掐,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時拾弃,就到了注釋b,而此時mPackageInfo是不為空的摆霉,所以會執(zhí)行mPackageInfo.getApplication()豪椿,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)携栋,
[圖片上傳中搭盾。。婉支。(35)]
[圖片上傳中鸯隅。。磅摹。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時滋迈, mApplication 還沒有被賦值,所以返回的是空户誓,只有把 attachBaseContext方法 執(zhí)行完成后困曙,mApplication 才會被賦值。
附圖一張:
[圖片上傳中侵歇。库快。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿悼潭,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果庇忌,并且深入源碼去分析遇到的問題。文章篇幅不短舰褪,希望能對大家有所幫助皆疹。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時,我們想在應用最早啟動時占拍,先做一些判斷略就,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務捎迫。
對其他應用提供服務指的是,我們的應用中有 ContentProvider表牢,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider窄绒,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時崔兴,我們的應用存在啟動和未啟動的兩種情況彰导。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中敲茄,但等到測試時發(fā)現(xiàn)了很多意想不到的情況位谋,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務折汞,而有時候第三方應用不能使等等的問題倔幼。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext爽待、onCreate损同、call 等)啟動順序,跟我們之前理解的稍稍不一樣鸟款。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后膏燃,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext何什、onCreate组哩、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用处渣,只包含這5個組件伶贰,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中罐栈。黍衙。。(3)]
MainService.java
[圖片上傳中荠诬。琅翻。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時柑贞,均已冷啟動的方式啟動應用方椎。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk钧嘶。
注意在測試的手機上棠众,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用有决,日志如下:

場景二摄欲,通過另外一個應用以啟動Service的形式啟動應用轿亮,其中啟動 MainService 的代碼如下:
[圖片上傳中。胸墙。。(8)]
日志如下:
[圖片上傳中按咒。迟隅。。(9)]
場景三励七,應用通過接受開機廣播啟動的方式啟動智袭,日志如下:
[圖片上傳中。掠抬。。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動锹雏,其中击儡,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。腰奋。单起。(11)]
日志如下:
[圖片上傳中。劣坊。嘀倒。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行局冰;
**3. **Activity测蘑、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的康二;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity碳胳、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎赠摇?
為了驗證這個問題固逗,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中藕帜。烫罩。。(13)]
我們再在上面第四種場景上進行驗證洽故,日志如下:
[圖片上傳中贝攒。。时甚。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后隘弊,才會執(zhí)行 Application 的 onCreate 的哈踱。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題梨熙,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中开镣。。咽扇。(15)]


我們還在第四個場景下驗證邪财,日志如下:
[圖片上傳中。质欲。树埠。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時嘶伟,ContentProvider 的 call方法 也在同時執(zhí)行怎憋。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**九昧。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎绊袋?
有,比如:Application所在類的構造方法耽装。為了驗證這個問題愤炸,將代碼改為:
[圖片上傳中。掉奄。规个。(18)]
程序啟動后,日志為:
[圖片上傳中姓建。诞仓。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用速兔。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢墅拭?有,自己可以再想想哦涣狗。

遇到的坑

好了谍婉,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊镀钓、功能或者參數(shù)穗熬,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯丁溅,一切感覺起來那么美好唤蔗,直到你運行程序崩潰時...
好吧好吧陶因,那些“坑”終于還是來了帽氓。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法洽腺。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時霜定,內(nèi)心是崩潰襟衰。
所以阵赠,如果在 attachBaseContext方法 中要使用 context 的話数初,那么使用 **this **吧,別再使用 getApplicationContext() 方法了作煌。下文有分析為什么诉稍。
“坑”二:這個其實不算很坑,也不會引起崩潰最疆,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider蚤告,那么這個 ContentProvider 會被初始化兩次努酸,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯杜恰,那么一定要檢查是否會有影響获诈。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法心褐,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法舔涎,Application 的代碼,Provider 的代碼逗爹,Activity 的代碼分別如下:
[圖片上傳中亡嫌。。掘而。(20)]
啟動應用后挟冠,日志如下:
[圖片上傳中。袍睡。知染。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)斑胜,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類控淡,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了止潘,現(xiàn)象掺炭、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢覆山?
我們通過看源碼竹伸,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null勋篓。
先看第一個問題吧享,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建譬嚣,已經(jīng)幫我們進行封裝钢颂,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼拜银,本文以下的源碼都以
6.0.1_r10
基礎殊鞭。
a. ActivityThread.java 的 main方法:
[圖片上傳中。尼桶。操灿。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。泵督。趾盐。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。小腊。救鲤。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。秩冈。本缠。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。入问。丹锹。(26)]
f. Application.java 的 attach方法
[圖片上傳中悠轩。青灼。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中田巴。麸折。锡凝。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的垢啼,還是在 ActivityThread.java 中:
[圖片上傳中窜锯。。芭析。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中锚扎。。馁启。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中驾孔。芍秆。。(31)]
看第二問題翠勉,為什么在我們自定義 Application 中的 attachBaseContext方法 中妖啥,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中对碌。荆虱。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中朽们。怀读。。(33)]
3. mPackageInfo 是什么時候賦值的呢骑脱?我們從 ContextImpl 實例化的地方入手菜枷,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)叁丧,果不其然犁跪,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。歹袁。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)寝优,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時条舔,就到了注釋b,而此時mPackageInfo是不為空的乏矾,所以會執(zhí)行mPackageInfo.getApplication()孟抗,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)钻心,
[圖片上傳中凄硼。。捷沸。(35)]
[圖片上傳中摊沉。。痒给。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時说墨, mApplication 還沒有被賦值,所以返回的是空苍柏,只有把 attachBaseContext方法 執(zhí)行完成后尼斧,mApplication 才會被賦值。
附圖一張:
[圖片上傳中试吁。棺棵。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果烛恤,并且深入源碼去分析遇到的問題母怜。文章篇幅不短,希望能對大家有所幫助棒动。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時糙申,我們想在應用最早啟動時,先做一些判斷船惨,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務柜裸。
對其他應用提供服務指的是,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider哆致,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果翅帜。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況铐然。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中恶座,但等到測試時發(fā)現(xiàn)了很多意想不到的情況搀暑,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務跨琳,而有時候第三方應用不能使等等的問題自点。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext脉让、onCreate桂敛、call 等)啟動順序,跟我們之前理解的稍稍不一樣溅潜。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后术唬,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext滚澜、onCreate粗仓、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用设捐,只包含這5個組件潦牛,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中挡育。巴碗。。(3)]
MainService.java
[圖片上傳中即寒。橡淆。召噩。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用逸爵。
冷啟動具滴,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上师倔,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一构韵,點擊桌面的圖標啟動應用,日志如下:

場景二趋艘,通過另外一個應用以啟動Service的形式啟動應用疲恢,其中啟動 MainService 的代碼如下:
[圖片上傳中。瓷胧。显拳。(8)]
日志如下:
[圖片上傳中。搓萧。杂数。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動瘸洛,日志如下:
[圖片上傳中揍移。。反肋。(10)]
場景四羊精,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中囚玫,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。读规。抓督。(11)]
日志如下:
[圖片上傳中。束亏。铃在。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行碍遍;
**3. **Activity定铜、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的怕敬;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity揣炕、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題,MainApplication 的代碼不變户敬,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中虏肾。弹谁。半醉。(13)]
我們再在上面第四種場景上進行驗證谚中,日志如下:
[圖片上傳中饰恕。牲览。墓陈。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的第献。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎贡必?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中痊硕。赊级。。(15)]
[圖片上傳中岔绸。理逊。。(16)]
我們還在第四個場景下驗證盒揉,日志如下:
[圖片上傳中晋被。。刚盈。(17)]
從日志中可以發(fā)現(xiàn)羡洛,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行藕漱。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行欲侮,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎肋联?
有威蕉,比如:Application所在類的構造方法。為了驗證這個問題橄仍,將代碼改為:
[圖片上傳中韧涨。。侮繁。(18)]
程序啟動后虑粥,日志為:
[圖片上傳中。宪哩。娩贷。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢锁孟?有育勺,自己可以再想想哦但荤。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“涧至,那么在項目中為了能”盡早“的提前初始化某些模塊腹躁、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中南蓬。嗯纺非,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧赘方,那些“坑”終于還是來了烧颖。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法窄陡。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時炕淮,內(nèi)心是崩潰。
所以跳夭,如果在 attachBaseContext方法 中要使用 context 的話涂圆,那么使用 **this **吧,別再使用 getApplicationContext() 方法了币叹。下文有分析為什么润歉。
“坑”二:這個其實不算很坑,也不會引起崩潰颈抚,但需要注意:
在 Application 的 attachBaseContext方法 中踩衩,去調(diào)用自身的 ContentProvider椰于,那么這個 ContentProvider 會被初始化兩次蚓胸,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯凿试,那么一定要檢查是否會有影響匹舞。
做一下驗證褐鸥,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法策菜,Application 的代碼,Provider 的代碼酒贬,Activity 的代碼分別如下:
[圖片上傳中又憨。。锭吨。(20)]
啟動應用后蠢莺,日志如下:
[圖片上傳中。零如。躏将。(21)]
可以看到锄弱,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類祸憋,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個会宪。

源碼分析

好了,現(xiàn)象蚯窥、問題和“坑”都經(jīng)歷了一遍掸鹅,那么 為什么 會是這樣呢?
我們通過看源碼拦赠,來跟蹤:
1. Application 的 attachBaseContext巍沙、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null荷鼠。
先看第一個問題句携,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建允乐,已經(jīng)幫我們進行封裝矮嫉,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼喳篇,本文以下的源碼都以
6.0.1_r10
基礎敞临。
a. ActivityThread.java 的 main方法:


b. ActivityThread.java 的 attach方法:
[圖片上傳中。麸澜。挺尿。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。炊邦。编矾。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。馁害。窄俏。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。碘菜。凹蜈。(26)]
f. Application.java 的 attach方法
[圖片上傳中。忍啸。仰坦。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。计雌。悄晃。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的凿滤,還是在 ActivityThread.java 中:
[圖片上傳中妈橄。庶近。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中眷蚓。鼻种。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中溪椎。普舆。。(31)]
看第二問題校读,為什么在我們自定義 Application 中的 attachBaseContext方法 中隶糕,調(diào)用 getApplicationContext() 為 null 呢官硝?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。。芬探。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中讼积。舰攒。熬荆。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手兔甘,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼谎碍,跟進代碼發(fā)現(xiàn),果不其然洞焙,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中蟆淀。。澡匪。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)熔任,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b唁情,而此時mPackageInfo是不為空的疑苔,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說甸鸟,mPackageInfo是LoadedApk的實例)惦费,
[圖片上傳中。抢韭。薪贫。(35)]
[圖片上傳中。篮绰。后雷。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時季惯, mApplication 還沒有被賦值吠各,所以返回的是空臀突,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值贾漏。
附圖一張:
[圖片上傳中候学。。纵散。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿梳码,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題伍掀。文章篇幅不短掰茶,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時蜜笤,我們想在應用最早啟動時濒蒋,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務把兔。
對其他應用提供服務指的是沪伙,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider县好,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果围橡。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況缕贡。
剛開始翁授,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況善绎,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”黔漂,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題禀酱。
于是我們跟蹤代碼炬守,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate剂跟、call 等)啟動順序减途,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后曹洽,我們才把遇到的問題徹底解決鳍置。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext揍愁、onCreate蝴悉、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用咆贬,只包含這5個組件,不考慮一個應用多進程的情況撞羽,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。衫冻。诀紊。(3)]
MainService.java
[圖片上傳中。隅俘。邻奠。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用为居。
冷啟動碌宴,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上蒙畴,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一唧喉,點擊桌面的圖標啟動應用,日志如下:

場景二忍抽,通過另外一個應用以啟動Service的形式啟動應用八孝,其中啟動 MainService 的代碼如下:
[圖片上傳中。鸠项。干跛。(8)]
日志如下:
[圖片上傳中。祟绊。楼入。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動牧抽,日志如下:
[圖片上傳中嘉熊。。扬舒。(10)]
場景四阐肤,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中讲坎,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中孕惜。。晨炕。(11)]
日志如下:
[圖片上傳中衫画。。瓮栗。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的削罩;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行瞄勾;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法弥激,是在 MainApplication 的 onCreate 方法之后執(zhí)行的丰榴;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)秆撮;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題换况,MainApplication 的代碼不變职辨,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。戈二。舒裤。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中觉吭。腾供。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后鲜滩,才會執(zhí)行 Application 的 onCreate 的伴鳖。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題徙硅,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中榜聂。。嗓蘑。(15)]
[圖片上傳中须肆。。嗓奢。(16)]
我們還在第四個場景下驗證蚀狰,日志如下:
[圖片上傳中借卧。。拒贱。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時佛嬉,ContentProvider 的 call方法 也在同時執(zhí)行柜思。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**巷燥。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎赡盘?
有,比如:Application所在類的構造方法缰揪。為了驗證這個問題陨享,將代碼改為:
[圖片上傳中葱淳。。抛姑。(18)]
程序啟動后赞厕,日志為:
[圖片上傳中。定硝。皿桑。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢蔬啡?有诲侮,自己可以再想想哦。

遇到的坑

好了箱蟆,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“沟绪,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)空猜,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中绽慈。嗯,一切感覺起來那么美好辈毯,直到你運行程序崩潰時...
好吧好吧坝疼,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中谆沃,使用了 getApplicationContext方法裙士。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰管毙。
所以腿椎,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧夭咬,別再使用 getApplicationContext() 方法了啃炸。下文有分析為什么。
“坑”二:這個其實不算很坑卓舵,也不會引起崩潰南用,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider掏湾,那么這個 ContentProvider 會被初始化兩次裹虫,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯融击,那么一定要檢查是否會有影響筑公。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法尊浪,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法匣屡,Application 的代碼封救,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中捣作。誉结。。(20)]
啟動應用后券躁,日志如下:
[圖片上傳中惩坑。。也拜。(21)]
可以看到以舒,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類搪泳,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個元莫。

源碼分析

好了悠抹,現(xiàn)象房匆、問題和“坑”都經(jīng)歷了一遍驹碍,那么 為什么 會是這樣呢尺棋?
我們通過看源碼影涉,來跟蹤:
1. Application 的 attachBaseContext莱褒、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序提佣。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null肚吏。
先看第一個問題方妖,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建罚攀,已經(jīng)幫我們進行封裝党觅,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼斋泄,本文以下的源碼都以
6.0.1_r10
基礎杯瞻。
a. ActivityThread.java 的 main方法:
[圖片上傳中。炫掐。魁莉。(22)]
b. ActivityThread.java 的 attach方法:


c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。募胃。旗唁。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。痹束。检疫。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。祷嘶。电谣。(26)]
f. Application.java 的 attach方法
[圖片上傳中秽梅。。剿牺。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中企垦。。晒来。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法钞诡,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中湃崩。荧降。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中攒读。朵诫。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中薄扁。剪返。。(31)]
看第二問題邓梅,為什么在我們自定義 Application 中的 attachBaseContext方法 中脱盲,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中日缨。钱反。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中匣距。面哥。。(33)]
3. mPackageInfo 是什么時候賦值的呢毅待?我們從 ContextImpl 實例化的地方入手幢竹,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)恩静,果不其然焕毫,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。驶乾。邑飒。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時级乐,就到了注釋b疙咸,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說芒划,mPackageInfo是LoadedApk的實例),
[圖片上傳中哼御。题山。兰粉。(35)]
[圖片上傳中。顶瞳。玖姑。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值慨菱,所以返回的是空焰络,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值符喝。
附圖一張:
[圖片上傳中闪彼。。协饲。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿畏腕,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題囱稽。文章篇幅不短郊尝,希望能對大家有所幫助二跋。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時战惊,我們想在應用最早啟動時,先做一些判斷扎即,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務吞获。
對其他應用提供服務指的是,我們的應用中有 ContentProvider谚鄙,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider各拷,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時闷营,我們的應用存在啟動和未啟動的兩種情況烤黍。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中傻盟,但等到測試時發(fā)現(xiàn)了很多意想不到的情況速蕊,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務娘赴,而有時候第三方應用不能使等等的問題规哲。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext诽表、onCreate唉锌、call 等)啟動順序隅肥,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后袄简,我們才把遇到的問題徹底解決腥放。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate痘番、call 等)被系統(tǒng)調(diào)用的順序捉片,我們創(chuàng)建一個簡單的應用,只包含這5個組件汞舱,不考慮一個應用多進程的情況伍纫,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。昂芜。莹规。(3)]
MainService.java
[圖片上傳中。泌神。良漱。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用欢际。
冷啟動母市,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上损趋,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一患久,點擊桌面的圖標啟動應用,日志如下:

場景二浑槽,通過另外一個應用以啟動Service的形式啟動應用蒋失,其中啟動 MainService 的代碼如下:
[圖片上傳中。。。(8)]
日志如下:
[圖片上傳中忿危。。铣卡。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動偏竟,日志如下:
[圖片上傳中煮落。。苫耸。(10)]
場景四州邢,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中量淌。骗村。。(11)]
日志如下:
[圖片上傳中呀枢。胚股。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的裙秋;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行琅拌;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法摘刑,是在 MainApplication 的 onCreate 方法之后執(zhí)行的进宝;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)枷恕;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎党晋?
為了驗證這個問題,MainApplication 的代碼不變徐块,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中未玻。。胡控。(13)]
我們再在上面第四種場景上進行驗證扳剿,日志如下:
[圖片上傳中。昼激。庇绽。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的癣猾。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎敛劝?
為了驗證這個問題余爆,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中纷宇。。蛾方。(15)]
[圖片上傳中像捶。。桩砰。(16)]
我們還在第四個場景下驗證拓春,日志如下:
[圖片上傳中。亚隅。硼莽。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時煮纵,ContentProvider 的 call方法 也在同時執(zhí)行懂鸵。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行偏螺,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎匆光?
有套像,比如:Application所在類的構造方法。為了驗證這個問題终息,將代碼改為:
[圖片上傳中夺巩。。周崭。(18)]
程序啟動后柳譬,日志為:
[圖片上傳中。续镇。征绎。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢磨取?有人柿,自己可以再想想哦挟憔。

遇到的坑

好了寞肖,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“双藕,那么在項目中為了能”盡早“的提前初始化某些模塊攻礼、功能或者參數(shù)抢野,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中睁宰。嗯智嚷,一切感覺起來那么美好咖摹,直到你運行程序崩潰時...
好吧好吧爹土,那些“坑”終于還是來了甥雕。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法胀茵。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時社露,內(nèi)心是崩潰。
所以琼娘,如果在 attachBaseContext方法 中要使用 context 的話峭弟,那么使用 **this **吧,別再使用 getApplicationContext() 方法了脱拼。下文有分析為什么瞒瘸。
“坑”二:這個其實不算很坑,也不會引起崩潰熄浓,但需要注意:
在 Application 的 attachBaseContext方法 中情臭,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate俯在。如果你在 ContentProvider 的 onCreate 中有一些邏輯丁侄,那么一定要檢查是否會有影響。
做一下驗證朝巫,在 Application 中調(diào)用 Provider 的 call方法鸿摇,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼劈猿,Provider 的代碼拙吉,Activity 的代碼分別如下:
[圖片上傳中。揪荣。筷黔。(20)]
啟動應用后,日志如下:
[圖片上傳中仗颈。佛舱。。(21)]
可以看到挨决,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)请祖,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個脖祈。

源碼分析

好了肆捕,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍盖高,那么 為什么 會是這樣呢慎陵?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext喻奥、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序席纽。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題撞蚕,我們知道Java進程的入口方法一般都是在main中润梯,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝诈豌,其實應用 main方法 是在 ActivityThread.java 中的仆救。
我們查看 ActivityThread.java 的源碼抒和,本文以下的源碼都以
6.0.1_r10
基礎矫渔。
a. ActivityThread.java 的 main方法:
[圖片上傳中。摧莽。庙洼。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。。油够。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中蚁袭。。颜屠。(24)]
d. LoaderApk.java 的 makeApplication方法


e. Instrumentation.java的相關方法
[圖片上傳中篮幢。捕传。。(26)]
f. Application.java 的 attach方法
[圖片上傳中删性。。焕窝。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中蹬挺。。它掂。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法巴帮,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中虐秋。榕茧。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中客给。雪猪。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中起愈。只恨。。(31)]
看第二問題抬虽,為什么在我們自定義 Application 中的 attachBaseContext方法 中官觅,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中阐污。休涤。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中笛辟。功氨。。(33)]
3. mPackageInfo 是什么時候賦值的呢手幢?我們從 ContextImpl 實例化的地方入手捷凄,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)围来,果不其然跺涤,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中匈睁。。桶错。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)航唆,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b院刁,而此時mPackageInfo是不為空的糯钙,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說退腥,mPackageInfo是LoadedApk的實例)超营,
[圖片上傳中。阅虫。演闭。(35)]
[圖片上傳中。颓帝。米碰。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值购城,所以返回的是空吕座,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值瘪板。
附圖一張:
[圖片上傳中吴趴。。侮攀。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿锣枝,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題兰英。文章篇幅不短撇叁,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時畦贸,我們想在應用最早啟動時陨闹,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務薄坏。
對其他應用提供服務指的是族购,我們的應用中有 ContentProvider客叉,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider今膊,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果芽突。當?shù)谌綉谜{(diào)用我們的應用時辣往,我們的應用存在啟動和未啟動的兩種情況教硫。
剛開始涧尿,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況帖蔓,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”矮瘟,但有時候第三方應用能夠使用服務瞳脓,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼澈侠,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext劫侧、onCreate、call 等)啟動順序哨啃,跟我們之前理解的稍稍不一樣烧栋。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決拳球。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext审姓、onCreate、call 等)被系統(tǒng)調(diào)用的順序祝峻,我們創(chuàng)建一個簡單的應用魔吐,只包含這5個組件,不考慮一個應用多進程的情況莱找,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中酬姆。。奥溺。(3)]
MainService.java
[圖片上傳中辞色。。浮定。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時相满,均已冷啟動的方式啟動應用。
冷啟動桦卒,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk雳灵。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一闸盔,點擊桌面的圖標啟動應用悯辙,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用迎吵,其中啟動 MainService 的代碼如下:
[圖片上傳中躲撰。。击费。(8)]
日志如下:
[圖片上傳中拢蛋。。蔫巩。(9)]
場景三谆棱,應用通過接受開機廣播啟動的方式啟動快压,日志如下:
[圖片上傳中。垃瞧。蔫劣。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動个从,其中脉幢,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。嗦锐。嫌松。(11)]
日志如下:
[圖片上傳中。奕污。萎羔。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行碳默;
**3. **Activity贾陷、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的腻窒;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity昵宇、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎儿子?
為了驗證這個問題烫幕,MainApplication 的代碼不變期丰,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中畜隶。享钞。。(13)]
我們再在上面第四種場景上進行驗證愉适,日志如下:
[圖片上傳中犯助。。维咸。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后剂买,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎癌蓖?
為了驗證這個問題瞬哼,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。租副。坐慰。(15)]
[圖片上傳中。用僧。结胀。(16)]
我們還在第四個場景下驗證赞咙,日志如下:
[圖片上傳中。糟港。攀操。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時着逐,ContentProvider 的 call方法 也在同時執(zhí)行崔赌。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行意蛀,而是會同時執(zhí)行**耸别。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有县钥,比如:Application所在類的構造方法秀姐。為了驗證這個問題,將代碼改為:
[圖片上傳中若贮。省有。。(18)]
程序啟動后谴麦,日志為:
[圖片上傳中蠢沿。。匾效。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用舷蟀。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有面哼,自己可以再想想哦野宜。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“魔策,那么在項目中為了能”盡早“的提前初始化某些模塊匈子、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中闯袒。嗯虎敦,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧政敢,那些“坑”終于還是來了其徙。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法堕仔。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時擂橘,內(nèi)心是崩潰。
所以摩骨,如果在 attachBaseContext方法 中要使用 context 的話通贞,那么使用 **this **吧朗若,別再使用 getApplicationContext() 方法了。下文有分析為什么昌罩。
“坑”二:這個其實不算很坑哭懈,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中茎用,去調(diào)用自身的 ContentProvider遣总,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate轨功。如果你在 ContentProvider 的 onCreate 中有一些邏輯番电,那么一定要檢查是否會有影響。
做一下驗證柔昼,在 Application 中調(diào)用 Provider 的 call方法侨艾,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼羡滑,Provider 的代碼菇爪,Activity 的代碼分別如下:
[圖片上傳中。柒昏。凳宙。(20)]
啟動應用后,日志如下:
[圖片上傳中职祷。氏涩。。(21)]
可以看到堪旧,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)削葱,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個淳梦。

源碼分析

好了析砸,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍爆袍,那么 為什么 會是這樣呢首繁?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext陨囊、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序弦疮。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題蜘醋,我們知道Java進程的入口方法一般都是在main中胁塞,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的啸罢。
我們查看 ActivityThread.java 的源碼编检,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中扰才。允懂。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中衩匣。蕾总。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:


d. LoaderApk.java 的 makeApplication方法
[圖片上傳中琅捏。生百。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中午绳。置侍。映之。(26)]
f. Application.java 的 attach方法
[圖片上傳中拦焚。。杠输。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中赎败。。蠢甲。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法僵刮,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中鹦牛。搞糕。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中曼追。窍仰。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中礼殊。驹吮。。(31)]
看第二問題晶伦,為什么在我們自定義 Application 中的 attachBaseContext方法 中碟狞,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中婚陪。族沃。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。脆淹。陪毡。(33)]
3. mPackageInfo 是什么時候賦值的呢颗圣?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)普办,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中师骗。通孽。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)拙友,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時为狸,就到了注釋b,而此時mPackageInfo是不為空的遗契,所以會執(zhí)行mPackageInfo.getApplication()辐棒,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)牍蜂,
[圖片上傳中漾根。。鲫竞。(35)]
[圖片上傳中。从绘。寄疏。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值僵井,所以返回的是空陕截,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值批什。
附圖一張:
[圖片上傳中农曲。。渊季。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿朋蔫,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題却汉。文章篇幅不短驯妄,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時合砂,我們想在應用最早啟動時青扔,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是微猖,我們的應用中有 ContentProvider谈息,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果凛剥。當?shù)谌綉谜{(diào)用我們的應用時侠仇,我們的應用存在啟動和未啟動的兩種情況。
剛開始犁珠,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中逻炊,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”犁享,但有時候第三方應用能夠使用服務余素,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼炊昆,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext桨吊、onCreate、call 等)啟動順序凤巨,跟我們之前理解的稍稍不一樣视乐。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決磅甩。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext炊林、onCreate、call 等)被系統(tǒng)調(diào)用的順序卷要,我們創(chuàng)建一個簡單的應用,只包含這5個組件独榴,不考慮一個應用多進程的情況僧叉,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。棺榔。瓶堕。(3)]
MainService.java
[圖片上傳中。症歇。郎笆。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時匾乓,均已冷啟動的方式啟動應用澈吨。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk惑惶。
注意在測試的手機上设塔,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一凄吏,點擊桌面的圖標啟動應用,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用痕钢,其中啟動 MainService 的代碼如下:
[圖片上傳中图柏。。任连。(8)]
日志如下:
[圖片上傳中蚤吹。。随抠。(9)]
場景三距辆,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中暮刃。跨算。。(10)]
場景四椭懊,其他應用調(diào)用 ContentProvider 的 call 方法啟動诸蚕,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中氧猬。背犯。。(11)]
日志如下:
[圖片上傳中盅抚。漠魏。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的妄均;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行柱锹;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法丰包,是在 MainApplication 的 onCreate 方法之后執(zhí)行的禁熏;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)邑彪;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎瞧毙?
為了驗證這個問題,MainApplication 的代碼不變寄症,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中宙彪。。有巧。(13)]
我們再在上面第四種場景上進行驗證释漆,日志如下:
[圖片上傳中。剪决。灵汪。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后檀训,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎享言?
為了驗證這個問題峻凫,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。览露。荧琼。(15)]
[圖片上傳中。差牛。命锄。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中偏化。脐恩。。(17)]
從日志中可以發(fā)現(xiàn)侦讨,Application 的 onCreate 執(zhí)行時驶冒,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行韵卤,而是會同時執(zhí)行**骗污。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有沈条,比如:Application所在類的構造方法需忿。為了驗證這個問題,將代碼改為:
[圖片上傳中蜡歹。敞葛。挪鹏。(18)]
程序啟動后柠傍,日志為:
[圖片上傳中辐啄。。景鼠。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢痹扇?有铛漓,自己可以再想想哦。

遇到的坑

好了鲫构,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“浓恶,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)结笨,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中包晰。嗯湿镀,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧伐憾,那些“坑”終于還是來了勉痴。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法树肃。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時蒸矛,內(nèi)心是崩潰。
所以胸嘴,如果在 attachBaseContext方法 中要使用 context 的話雏掠,那么使用 **this **吧,別再使用 getApplicationContext() 方法了劣像。下文有分析為什么乡话。
“坑”二:這個其實不算很坑,也不會引起崩潰耳奕,但需要注意:
在 Application 的 attachBaseContext方法 中绑青,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次吮铭,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate时迫。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響谓晌。
做一下驗證掠拳,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法纸肉,Application 的代碼溺欧,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中柏肪。姐刁。。(20)]
啟動應用后烦味,日志如下:
[圖片上傳中聂使。。谬俄。(21)]
可以看到柏靶,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類溃论,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個屎蜓。

源碼分析

好了,現(xiàn)象钥勋、問題和“坑”都經(jīng)歷了一遍炬转,那么 為什么 會是這樣呢辆苔?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext扼劈、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序驻啤。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題测僵,我們知道Java進程的入口方法一般都是在main中街佑,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝捍靠,其實應用 main方法 是在 ActivityThread.java 中的沐旨。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎榨婆。
a. ActivityThread.java 的 main方法:
[圖片上傳中磁携。。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。荆秦。凿跳。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中岩臣。。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。粮呢。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中钞艇。啄寡。。(26)]
f. Application.java 的 attach方法
[圖片上傳中哩照。挺物。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:


h. 繼續(xù)跟蹤 installContentProviders 這個方法飘弧,而這個方法是會調(diào)用到 installProvider方法 中的识藤,還是在 ActivityThread.java 中:
[圖片上傳中。次伶。蹋岩。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。学少。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中秧骑。版确。扣囊。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中绒疗,調(diào)用 getApplicationContext() 為 null 呢侵歇?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。吓蘑。惕虑。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。磨镶。溃蔫。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手琳猫,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼伟叛,跟進代碼發(fā)現(xiàn),果不其然脐嫂,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中统刮。。账千。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)侥蒙,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b匀奏,而此時mPackageInfo是不為空的鞭衩,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說攒射,mPackageInfo是LoadedApk的實例)醋旦,
[圖片上傳中。会放。饲齐。(35)]
[圖片上傳中。咧最。捂人。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值矢沿,所以返回的是空滥搭,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值捣鲸。
附圖一張:
[圖片上傳中瑟匆。。栽惶。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿愁溜,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果疾嗅,并且深入源碼去分析遇到的問題。文章篇幅不短冕象,希望能對大家有所幫助代承。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時,我們想在應用最早啟動時渐扮,先做一些判斷囱淋,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務涨岁。
對其他應用提供服務指的是拯钻,我們的應用中有 ContentProvider躁倒,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果只锻。當?shù)谌綉谜{(diào)用我們的應用時玖像,我們的應用存在啟動和未啟動的兩種情況。
剛開始齐饮,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中捐寥,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”祖驱,但有時候第三方應用能夠使用服務握恳,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼捺僻,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext乡洼、onCreate、call 等)啟動順序匕坯,跟我們之前理解的稍稍不一樣束昵。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決葛峻。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext锹雏、onCreate、call 等)被系統(tǒng)調(diào)用的順序术奖,我們創(chuàng)建一個簡單的應用礁遵,只包含這5個組件,不考慮一個應用多進程的情況采记,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中佣耐。。唧龄。(3)]
MainService.java
[圖片上傳中兼砖。。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時掖鱼,均已冷啟動的方式啟動應用然走。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk戏挡。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一晨仑,點擊桌面的圖標啟動應用褐墅,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用洪己,其中啟動 MainService 的代碼如下:
[圖片上傳中妥凳。。答捕。(8)]
日志如下:
[圖片上傳中逝钥。。拱镐。(9)]
場景三艘款,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中沃琅。哗咆。。(10)]
場景四益眉,其他應用調(diào)用 ContentProvider 的 call 方法啟動晌柬,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中郭脂。年碘。。(11)]
日志如下:
[圖片上傳中展鸡。屿衅。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的娱颊;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行傲诵;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法箱硕,是在 MainApplication 的 onCreate 方法之后執(zhí)行的拴竹;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)剧罩;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎栓拜?
為了驗證這個問題雏吭,MainApplication 的代碼不變城侧,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。。薄嫡。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中涣狗。蝉衣。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后诫给,才會執(zhí)行 Application 的 onCreate 的香拉。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題中狂,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中凫碌。。胃榕。(15)]
[圖片上傳中盛险。。勋又。(16)]
我們還在第四個場景下驗證苦掘,日志如下:
[圖片上傳中。赐写。鸟蜡。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時挺邀,ContentProvider 的 call方法 也在同時執(zhí)行揉忘。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**端铛。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎泣矛?
有,比如:Application所在類的構造方法禾蚕。為了驗證這個問題您朽,將代碼改為:
[圖片上傳中。换淆。哗总。(18)]
程序啟動后,日志為:
[圖片上傳中倍试。讯屈。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用县习。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢涮母?有谆趾,自己可以再想想哦。

遇到的坑

好了叛本,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“沪蓬,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)来候,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中跷叉。嗯,一切感覺起來那么美好吠勘,直到你運行程序崩潰時...
好吧好吧性芬,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中剧防,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時辫樱,內(nèi)心是崩潰峭拘。
所以,如果在 attachBaseContext方法 中要使用 context 的話狮暑,那么使用 **this **吧鸡挠,別再使用 getApplicationContext() 方法了。下文有分析為什么搬男。
“坑”二:這個其實不算很坑拣展,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中缔逛,去調(diào)用自身的 ContentProvider备埃,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate褐奴。如果你在 ContentProvider 的 onCreate 中有一些邏輯按脚,那么一定要檢查是否會有影響。
做一下驗證敦冬,在 Application 中調(diào)用 Provider 的 call方法辅搬,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼甫贯,Provider 的代碼朴皆,Activity 的代碼分別如下:
[圖片上傳中蚂子。。溶褪。(20)]
啟動應用后,日志如下:
[圖片上傳中踊兜。竿滨。佳恬。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)于游,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類毁葱,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了贰剥,現(xiàn)象倾剿、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢蚌成?
我們通過看源碼前痘,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序担忧。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null芹缔。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中瓶盛,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建最欠,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的惩猫。
我們查看 ActivityThread.java 的源碼芝硬,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中轧房。拌阴。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中奶镶。迟赃。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中实辑。捺氢。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中剪撬。摄乒。。(25)]
e. Instrumentation.java的相關方法


f. Application.java 的 attach方法
[圖片上傳中残黑。馍佑。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中梨水。拭荤。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法疫诽,而這個方法是會調(diào)用到 installProvider方法 中的舅世,還是在 ActivityThread.java 中:
[圖片上傳中旦委。。雏亚。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中缨硝。。罢低。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中查辩。。网持。(31)]
看第二問題宜岛,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢功舀?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中萍倡。。辟汰。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中遣铝。。莉擒。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手瘫絮,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼涨冀,跟進代碼發(fā)現(xiàn),果不其然麦萤,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。。钮呀。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)荣德,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b命满,而此時mPackageInfo是不為空的涝滴,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說胶台,mPackageInfo是LoadedApk的實例)歼疮,
[圖片上傳中。诈唬。韩脏。(35)]
[圖片上傳中。铸磅。赡矢。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時杭朱, mApplication 還沒有被賦值,所以返回的是空吹散,只有把 attachBaseContext方法 執(zhí)行完成后弧械,mApplication 才會被賦值。
附圖一張:
[圖片上傳中送浊。梦谜。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿袭景,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果唁桩,并且深入源碼去分析遇到的問題。文章篇幅不短耸棒,希望能對大家有所幫助荒澡。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時,我們想在應用最早啟動時与殃,先做一些判斷单山,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是幅疼,我們的應用中有 ContentProvider米奸,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果爽篷。當?shù)谌綉谜{(diào)用我們的應用時悴晰,我們的應用存在啟動和未啟動的兩種情況。
剛開始逐工,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中铡溪,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”泪喊,但有時候第三方應用能夠使用服務棕硫,而有時候第三方應用不能使等等的問題瘤泪。
于是我們跟蹤代碼按声,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate、call 等)啟動順序拔第,跟我們之前理解的稍稍不一樣闲坎。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后艘虎,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext绣檬、onCreate嫡良、call 等)被系統(tǒng)調(diào)用的順序墨林,我們創(chuàng)建一個簡單的應用而姐,只包含這5個組件刽虹,不考慮一個應用多進程的情況局齿,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中份企。钞脂。莲镣。(3)]
MainService.java
[圖片上傳中。。因妇。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時砰左,均已冷啟動的方式啟動應用场航。
冷啟動缠导,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上旗闽,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一酬核,點擊桌面的圖標啟動應用,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中抒寂。踢涌。。(8)]
日志如下:
[圖片上傳中。。。(9)]
場景三旧巾,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中忍些。鲁猩。。(10)]
場景四罢坝,其他應用調(diào)用 ContentProvider 的 call 方法啟動廓握,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中嘁酿。隙券。。(11)]
日志如下:
[圖片上傳中闹司。娱仔。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的游桩;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行牲迫;
**3. **Activity耐朴、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法脚乡,是在 MainApplication 的 onCreate 方法之后執(zhí)行的辆飘;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity功偿、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎陕贮?
為了驗證這個問題,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中看疙。承耿。骡湖。(13)]
我們再在上面第四種場景上進行驗證收班,日志如下:
[圖片上傳中。。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后噪沙,才會執(zhí)行 Application 的 onCreate 的达箍。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題融虽,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中有额。。。(15)]
[圖片上傳中巍佑。茴迁。。(16)]
我們還在第四個場景下驗證萤衰,日志如下:
[圖片上傳中堕义。。脆栋。(17)]
從日志中可以發(fā)現(xiàn)倦卖,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行椿争。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行怕膛,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎秦踪?
有褐捻,比如:Application所在類的構造方法。為了驗證這個問題椅邓,將代碼改為:
[圖片上傳中柠逞。。景馁。(18)]
程序啟動后板壮,日志為:
[圖片上傳中。合住。绰精。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢聊疲?有茬底,自己可以再想想哦沪悲。

遇到的坑

好了获洲,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“喉脖,那么在項目中為了能”盡早“的提前初始化某些模塊帆精、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中铃岔。嗯涉馁,一切感覺起來那么美好门岔,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了烤送。
“坑”一:在 Application 的 attachBaseContext方法 中寒随,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰妻往。
所以互艾,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧讯泣,別再使用 getApplicationContext() 方法了纫普。下文有分析為什么。
“坑”二:這個其實不算很坑好渠,也不會引起崩潰昨稼,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider拳锚,那么這個 ContentProvider 會被初始化兩次假栓,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯晌畅,那么一定要檢查是否會有影響但指。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法抗楔,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法棋凳,Application 的代碼,Provider 的代碼连躏,Activity 的代碼分別如下:
[圖片上傳中剩岳。。入热。(20)]
啟動應用后拍棕,日志如下:
[圖片上傳中。勺良。绰播。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)尚困,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類蠢箩,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了事甜,現(xiàn)象谬泌、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢逻谦?
我們通過看源碼掌实,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序邦马。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null贱鼻。
先看第一個問題宴卖,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建邻悬,已經(jīng)幫我們進行封裝嘱腥,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼拘悦,本文以下的源碼都以
6.0.1_r10
基礎齿兔。
a. ActivityThread.java 的 main方法:
[圖片上傳中。础米。分苇。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。屁桑。医寿。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。蘑斧。靖秩。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。竖瘾。沟突。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。捕传。惠拭。(26)]
f. Application.java 的 attach方法
[圖片上傳中。庸论。喂分。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中梅肤。澈圈。棺弊。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的鱼喉,還是在 ActivityThread.java 中:


i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中秀鞭。。蒲凶。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中气筋。拆内。旋圆。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中麸恍,調(diào)用 getApplicationContext() 為 null 呢灵巧?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中搀矫。。刻肄。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中瓤球。。敏弃。(33)]
3. mPackageInfo 是什么時候賦值的呢卦羡?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼麦到,跟進代碼發(fā)現(xiàn)绿饵,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中瓶颠。拟赊。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)粹淋,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時吸祟,就到了注釋b,而此時mPackageInfo是不為空的桃移,所以會執(zhí)行mPackageInfo.getApplication()屋匕,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)借杰,
[圖片上傳中炒瘟。。第步。(35)]
[圖片上傳中疮装。。粘都。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時廓推, mApplication 還沒有被賦值,所以返回的是空翩隧,只有把 attachBaseContext方法 執(zhí)行完成后樊展,mApplication 才會被賦值。
附圖一張:
[圖片上傳中堆生。专缠。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿淑仆,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果涝婉,并且深入源碼去分析遇到的問題。文章篇幅不短蔗怠,希望能對大家有所幫助墩弯。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時吩跋,我們想在應用最早啟動時,先做一些判斷渔工,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務锌钮。
對其他應用提供服務指的是,我們的應用中有 ContentProvider引矩,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider梁丘,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時旺韭,我們的應用存在啟動和未啟動的兩種情況兰吟。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中茂翔,但等到測試時發(fā)現(xiàn)了很多意想不到的情況混蔼,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務珊燎,而有時候第三方應用不能使等等的問題惭嚣。
于是我們跟蹤代碼罚拟,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext门烂、onCreate拴孤、call 等)啟動順序宠纯,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后谒府,我們才把遇到的問題徹底解決烧栋。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext并鸵、onCreate芦瘾、call 等)被系統(tǒng)調(diào)用的順序捌蚊,我們創(chuàng)建一個簡單的應用,只包含這5個組件近弟,不考慮一個應用多進程的情況缅糟,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。祷愉。窗宦。(3)]
MainService.java
[圖片上傳中。二鳄。赴涵。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用订讼。
冷啟動髓窜,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上躯嫉,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一纱烘,點擊桌面的圖標啟動應用,日志如下:

場景二祈餐,通過另外一個應用以啟動Service的形式啟動應用擂啥,其中啟動 MainService 的代碼如下:
[圖片上傳中。帆阳。哺壶。(8)]
日志如下:
[圖片上傳中。蜒谤。山宾。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動鳍徽,日志如下:
[圖片上傳中资锰。。阶祭。(10)]
場景四绷杜,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中濒募,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中鞭盟。。瑰剃。(11)]
日志如下:
[圖片上傳中齿诉。。晌姚。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的粤剧;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity挥唠、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法俊扳,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity猛遍、Service 等的 onCreate(Activity 和 Service 不分先后)馋记;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題懊烤,MainApplication 的代碼不變梯醒,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。腌紧。茸习。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中壁肋。号胚。籽慢。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的猫胁。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎箱亿?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中弃秆。届惋。瘟栖。(15)]
[圖片上傳中箱吕。。馁蒂。(16)]
我們還在第四個場景下驗證衡查,日志如下:
[圖片上傳中瘩欺。。拌牲。(17)]
從日志中可以發(fā)現(xiàn)击碗,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行们拙。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行稍途,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎砚婆?
有械拍,比如:Application所在類的構造方法。為了驗證這個問題装盯,將代碼改為:
[圖片上傳中坷虑。。埂奈。(18)]
程序啟動后迄损,日志為:
[圖片上傳中。账磺。芹敌。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢垮抗?有氏捞,自己可以再想想哦。

遇到的坑

好了冒版,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“液茎,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中捆等。嗯滞造,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧栋烤,那些“坑”終于還是來了谒养。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法班缎。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時蝴光,內(nèi)心是崩潰她渴。
所以达址,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧趁耗,別再使用 getApplicationContext() 方法了沉唠。下文有分析為什么。
“坑”二:這個其實不算很坑苛败,也不會引起崩潰满葛,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider罢屈,那么這個 ContentProvider 會被初始化兩次嘀韧,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯缠捌,那么一定要檢查是否會有影響锄贷。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法曼月,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法谊却,Application 的代碼,Provider 的代碼哑芹,Activity 的代碼分別如下:
[圖片上傳中炎辨。。聪姿。(20)]
啟動應用后碴萧,日志如下:
[圖片上傳中。末购。讳推。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)玩般,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類银觅,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個熙侍。

源碼分析

好了晤柄,現(xiàn)象哥攘、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢卧晓?
我們通過看源碼骗灶,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序闻鉴。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null授艰。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝隅很,其實應用 main方法 是在 ActivityThread.java 中的件相。
我們查看 ActivityThread.java 的源碼喷众,本文以下的源碼都以
6.0.1_r10
基礎裁替。
a. ActivityThread.java 的 main方法:
[圖片上傳中蘸际。察藐。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中灌具。塔沃。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。。巩梢。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。荚斯。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。光戈。低飒。(26)]
f. Application.java 的 attach方法
[圖片上傳中。镜豹。沈自。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。夏块。蛤肌。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的拌屏,還是在 ActivityThread.java 中:
[圖片上傳中看靠。夹姥。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。虚汛。秕重。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法


看第二問題不同,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢溶耘?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中二拐。。凳兵。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中百新。。庐扫。(33)]
3. mPackageInfo 是什么時候賦值的呢饭望?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼形庭,跟進代碼發(fā)現(xiàn)铅辞,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中萨醒。斟珊。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)富纸,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時囤踩,就到了注釋b旨椒,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()堵漱,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說综慎,mPackageInfo是LoadedApk的實例),
[圖片上傳中勤庐。示惊。。(35)]
[圖片上傳中愉镰。涝涤。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時岛杀, mApplication 還沒有被賦值阔拳,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后类嗤,mApplication 才會被賦值宠进。
附圖一張:
[圖片上傳中鹃操。才避。迎吵。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果精偿,并且深入源碼去分析遇到的問題弧圆。文章篇幅不短,希望能對大家有所幫助笔咽。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時搔预,我們想在應用最早啟動時,先做一些判斷叶组,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務拯田。
對其他應用提供服務指的是,我們的應用中有 ContentProvider甩十,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider船庇,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時侣监,我們的應用存在啟動和未啟動的兩種情況鸭轮。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中橄霉,但等到測試時發(fā)現(xiàn)了很多意想不到的情況窃爷,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題吞鸭。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext覆糟、onCreate刻剥、call 等)啟動順序,跟我們之前理解的稍稍不一樣滩字。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后造虏,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext麦箍、onCreate漓藕、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用挟裂,只包含這5個組件享钞,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中诀蓉。栗竖。。(3)]
MainService.java
[圖片上傳中渠啤。狐肢。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時沥曹,均已冷啟動的方式啟動應用份名。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk妓美。
注意在測試的手機上僵腺,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用壶栋,日志如下:

場景二想邦,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中委刘。丧没。。(8)]
日志如下:
[圖片上傳中呕童。。淆珊。(9)]
場景三夺饲,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中。往声。擂找。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動浩销,其中贯涎,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。慢洋。塘雳。(11)]
日志如下:
[圖片上傳中。普筹。败明。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行太防;
**3. **Activity郑兴、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法猪半,是在 MainApplication 的 onCreate 方法之后執(zhí)行的路召;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity代乃、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎醇王?
為了驗證這個問題呢燥,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中寓娩。叛氨。。(13)]
我們再在上面第四種場景上進行驗證棘伴,日志如下:
[圖片上傳中寞埠。。焊夸。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后仁连,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎阱穗?
為了驗證這個問題饭冬,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。揪阶。昌抠。(15)]
[圖片上傳中。鲁僚。炊苫。(16)]
我們還在第四個場景下驗證裁厅,日志如下:
[圖片上傳中。侨艾。执虹。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時唠梨,ContentProvider 的 call方法 也在同時執(zhí)行袋励。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**姻成。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有愿棋,比如:Application所在類的構造方法科展。為了驗證這個問題才睹,將代碼改為:
[圖片上傳中蜂科。。短条。(18)]
程序啟動后导匣,日志為:
[圖片上傳中。茸时。贡定。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢可都?有厕氨,自己可以再想想哦。

遇到的坑

好了汹粤,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“命斧,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)嘱兼,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中国葬。嗯,一切感覺起來那么美好芹壕,直到你運行程序崩潰時...
好吧好吧汇四,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中踢涌,使用了 getApplicationContext方法通孽。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰睁壁。
所以背苦,如果在 attachBaseContext方法 中要使用 context 的話互捌,那么使用 **this **吧,別再使用 getApplicationContext() 方法了行剂。下文有分析為什么秕噪。
“坑”二:這個其實不算很坑,也不會引起崩潰厚宰,但需要注意:
在 Application 的 attachBaseContext方法 中腌巾,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次铲觉,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate澈蝙。如果你在 ContentProvider 的 onCreate 中有一些邏輯田篇,那么一定要檢查是否會有影響展姐。
做一下驗證郑什,在 Application 中調(diào)用 Provider 的 call方法赎瞎,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼独悴,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中。况褪。。(20)]
啟動應用后更耻,日志如下:
[圖片上傳中测垛。。秧均。(21)]
可以看到食侮,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類目胡,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個锯七。

源碼分析

好了,現(xiàn)象誉己、問題和“坑”都經(jīng)歷了一遍眉尸,那么 為什么 會是這樣呢?
我們通過看源碼巨双,來跟蹤:
1. Application 的 attachBaseContext噪猾、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null筑累。
先看第一個問題袱蜡,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建慢宗,已經(jīng)幫我們進行封裝坪蚁,其實應用 main方法 是在 ActivityThread.java 中的奔穿。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎迅细。
a. ActivityThread.java 的 main方法:
[圖片上傳中巫橄。。茵典。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。彩倚。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中扶平。帆离。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中结澄。哥谷。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中麻献。们妥。。(26)]
f. Application.java 的 attach方法
[圖片上傳中勉吻。监婶。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中齿桃。惑惶。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法短纵,而這個方法是會調(diào)用到 installProvider方法 中的带污,還是在 ActivityThread.java 中:
[圖片上傳中。香到。鱼冀。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)


j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。养渴。雷绢。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中理卑,調(diào)用 getApplicationContext() 為 null 呢翘紊?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。藐唠。帆疟。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中鹉究。。踪宠。(33)]
3. mPackageInfo 是什么時候賦值的呢自赔?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼绍妨,跟進代碼發(fā)現(xiàn),果不其然柬脸,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中洁灵。宾巍。拜鹤。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)丰包,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b垦巴,而此時mPackageInfo是不為空的媳搪,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說骤宣,mPackageInfo是LoadedApk的實例)秦爆,
[圖片上傳中。涯雅。鲜结。(35)]
[圖片上傳中展运。活逆。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時拗胜, mApplication 還沒有被賦值蔗候,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后埂软,mApplication 才會被賦值锈遥。
附圖一張:
[圖片上傳中。勘畔。所灸。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果炫七,并且深入源碼去分析遇到的問題爬立。文章篇幅不短,希望能對大家有所幫助万哪。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時侠驯,我們想在應用最早啟動時抡秆,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務吟策。
對其他應用提供服務指的是儒士,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider檩坚,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果着撩。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況匾委。
剛開始睹酌,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況剩檀,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”憋沿,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題沪猴。
于是我們跟蹤代碼辐啄,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate运嗜、call 等)啟動順序壶辜,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后担租,我們才把遇到的問題徹底解決砸民。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate奋救、call 等)被系統(tǒng)調(diào)用的順序岭参,我們創(chuàng)建一個簡單的應用,只包含這5個組件,不考慮一個應用多進程的情況色解,代碼分別為:


MainApplication.java

MainActivity.java

MainService.java
[圖片上傳中茂嗓。。冒签。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時在抛,均已冷啟動的方式啟動應用钟病。
冷啟動萧恕,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上肠阱,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一票唆,點擊桌面的圖標啟動應用,日志如下:

場景二屹徘,通過另外一個應用以啟動Service的形式啟動應用走趋,其中啟動 MainService 的代碼如下:
[圖片上傳中。噪伊。簿煌。(8)]
日志如下:
[圖片上傳中。。嗡载。(9)]
場景三闷供,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中夺荒。瞒渠。。(10)]
場景四技扼,其他應用調(diào)用 ContentProvider 的 call 方法啟動伍玖,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中剿吻。窍箍。。(11)]
日志如下:
[圖片上傳中丽旅。仔燕。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的魔招;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行晰搀;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法办斑,是在 MainApplication 的 onCreate 方法之后執(zhí)行的外恕;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)乡翅;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎鳞疲?
為了驗證這個問題,MainApplication 的代碼不變蠕蚜,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中尚洽。。靶累。(13)]
我們再在上面第四種場景上進行驗證腺毫,日志如下:
[圖片上傳中。挣柬。潮酒。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的邪蛔。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎急黎?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。勃教。淤击。(15)]
[圖片上傳中。故源。遭贸。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中心软。樱溉。。(17)]
從日志中可以發(fā)現(xiàn)纬凤,Application 的 onCreate 執(zhí)行時福贞,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行停士,而是會同時執(zhí)行**挖帘。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有恋技,比如:Application所在類的構造方法拇舀。為了驗證這個問題,將代碼改為:
[圖片上傳中蜻底。骄崩。。(18)]
程序啟動后薄辅,日志為:
[圖片上傳中要拂。。站楚。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用脱惰。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有窿春,自己可以再想想哦枪芒。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“谁尸,那么在項目中為了能”盡早“的提前初始化某些模塊舅踪、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中良蛮。嗯抽碌,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧决瞳,那些“坑”終于還是來了货徙。
“坑”一:在 Application 的 attachBaseContext方法 中儡率,使用了 getApplicationContext方法吐绵。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時绢涡,內(nèi)心是崩潰轻掩。
所以拂檩,如果在 attachBaseContext方法 中要使用 context 的話硼身,那么使用 **this **吧笆凌,別再使用 getApplicationContext() 方法了答捕。下文有分析為什么。
“坑”二:這個其實不算很坑泻仙,也不會引起崩潰糕再,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider玉转,那么這個 ContentProvider 會被初始化兩次突想,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯究抓,那么一定要檢查是否會有影響猾担。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法刺下,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法绑嘹,Application 的代碼,Provider 的代碼怠李,Activity 的代碼分別如下:
[圖片上傳中圾叼。。捺癞。(20)]
啟動應用后夷蚊,日志如下:
[圖片上傳中。髓介。惕鼓。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)唐础,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類箱歧,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了一膨,現(xiàn)象呀邢、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢豹绪?
我們通過看源碼价淌,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序瞒津。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null蝉衣。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中巷蚪,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建病毡,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的屁柏。
我們查看 ActivityThread.java 的源碼啦膜,本文以下的源碼都以
6.0.1_r10
基礎有送。
a. ActivityThread.java 的 main方法:
[圖片上傳中。娶眷。似嗤。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中啸臀。。烁落。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中乘粒。。伤塌。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中灯萍。。每聪。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中旦棉。。药薯。(26)]
f. Application.java 的 attach方法
[圖片上傳中绑洛。。童本。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中真屯。。穷娱。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法绑蔫,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中泵额。配深。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中嫁盲。第美。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中女蜈。怜跑。。(31)]
看第二問題锥腻,為什么在我們自定義 Application 中的 attachBaseContext方法 中嗦董,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中瘦黑。京革。奇唤。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。匹摇。咬扇。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手廊勃,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼懈贺,跟進代碼發(fā)現(xiàn),果不其然坡垫,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中梭灿。。冰悠。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)堡妒,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b溉卓,而此時mPackageInfo是不為空的皮迟,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說桑寨,mPackageInfo是LoadedApk的實例)伏尼,
[圖片上傳中。西疤。烦粒。(35)]
[圖片上傳中。代赁。扰她。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值芭碍,所以返回的是空徒役,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值窖壕。
附圖一張:
[圖片上傳中忧勿。。瞻讽。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿鸳吸,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題速勇。文章篇幅不短晌砾,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時烦磁,我們想在應用最早啟動時养匈,先做一些判斷哼勇,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是呕乎,我們的應用中有 ContentProvider积担,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果猬仁。當?shù)谌綉谜{(diào)用我們的應用時帝璧,我們的應用存在啟動和未啟動的兩種情況。
剛開始逐虚,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中聋溜,但等到測試時發(fā)現(xiàn)了很多意想不到的情況谆膳,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”叭爱,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題漱病。
于是我們跟蹤代碼买雾,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate杨帽、call 等)啟動順序漓穿,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后注盈,我們才把遇到的問題徹底解決晃危。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate老客、call 等)被系統(tǒng)調(diào)用的順序僚饭,我們創(chuàng)建一個簡單的應用,只包含這5個組件胧砰,不考慮一個應用多進程的情況混巧,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中潮改。。。(3)]
MainService.java
[圖片上傳中倦畅。。峡懈。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時查辩,均已冷啟動的方式啟動應用。
冷啟動眠副,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk画切。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一侦啸,點擊桌面的圖標啟動應用槽唾,日志如下:

場景二丧枪,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中庞萍。拧烦。。(8)]
日志如下:
[圖片上傳中钝计。恋博。。(9)]
場景三私恬,應用通過接受開機廣播啟動的方式啟動债沮,日志如下:
[圖片上傳中。本鸣。疫衩。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動荣德,其中闷煤,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。涮瞻。鲤拿。(11)]
日志如下:
[圖片上傳中。署咽。近顷。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行宁否;
**3. **Activity窒升、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的家淤;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity异剥、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎絮重?
為了驗證這個問題冤寿,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中青伤。督怜。。(13)]
我們再在上面第四種場景上進行驗證狠角,日志如下:
[圖片上傳中号杠。。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后姨蟋,才會執(zhí)行 Application 的 onCreate 的屉凯。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題眼溶,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中悠砚。。堂飞。(15)]
[圖片上傳中灌旧。。绰筛。(16)]
我們還在第四個場景下驗證枢泰,日志如下:
[圖片上傳中。铝噩。衡蚂。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時薄榛,ContentProvider 的 call方法 也在同時執(zhí)行讳窟。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**敞恋。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有谋右,比如:Application所在類的構造方法硬猫。為了驗證這個問題,將代碼改為:
[圖片上傳中改执。。。(18)]
程序啟動后读规,日志為:
[圖片上傳中按声。。终蒂。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用蜂林。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有拇泣,自己可以再想想哦噪叙。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“霉翔,那么在項目中為了能”盡早“的提前初始化某些模塊睁蕾、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯子眶,一切感覺起來那么美好瀑凝,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了臭杰。
“坑”一:在 Application 的 attachBaseContext方法 中猜丹,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時硅卢,內(nèi)心是崩潰射窒。
所以,如果在 attachBaseContext方法 中要使用 context 的話将塑,那么使用 **this **吧脉顿,別再使用 getApplicationContext() 方法了。下文有分析為什么点寥。
“坑”二:這個其實不算很坑艾疟,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中敢辩,去調(diào)用自身的 ContentProvider蔽莱,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate戚长。如果你在 ContentProvider 的 onCreate 中有一些邏輯盗冷,那么一定要檢查是否會有影響。
做一下驗證同廉,在 Application 中調(diào)用 Provider 的 call方法仪糖,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼迫肖,Provider 的代碼锅劝,Activity 的代碼分別如下:
[圖片上傳中。蟆湖。故爵。(20)]
啟動應用后,日志如下:
[圖片上傳中隅津。诬垂。。(21)]
可以看到饥瓷,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)剥纷,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個呢铆。

源碼分析

好了晦鞋,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢悠垛?
我們通過看源碼线定,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序确买。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null斤讥。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中湾趾,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建芭商,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的搀缠。
我們查看 ActivityThread.java 的源碼铛楣,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中艺普。簸州。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中歧譬。岸浑。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中瑰步。矢洲。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中面氓。兵钮。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。。反砌。(26)]
f. Application.java 的 attach方法
[圖片上傳中搁凸。。睦焕。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中藐握。。垃喊。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法猾普,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中本谜。初家。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。溜在。陌知。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。掖肋。仆葡。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中志笼,調(diào)用 getApplicationContext() 為 null 呢沿盅?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。纫溃。腰涧。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:


3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手皇耗,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼南窗,跟進代碼發(fā)現(xiàn),果不其然郎楼,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中万伤。。呜袁。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)敌买,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b阶界,而此時mPackageInfo是不為空的虹钮,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說膘融,mPackageInfo是LoadedApk的實例)芙粱,
[圖片上傳中。氧映。。(35)]
[圖片上傳中岛都。律姨。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時臼疫, mApplication 還沒有被賦值择份,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后烫堤,mApplication 才會被賦值荣赶。
附圖一張:
[圖片上傳中凤价。。讯壶。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿料仗,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題伏蚊。文章篇幅不短立轧,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時躏吊,我們想在應用最早啟動時氛改,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務比伏。
對其他應用提供服務指的是胜卤,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider赁项,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果葛躏。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況悠菜。
剛開始舰攒,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況悔醋,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”摩窃,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題芬骄。
于是我們跟蹤代碼猾愿,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate账阻、call 等)啟動順序裕照,跟我們之前理解的稍稍不一樣殖蚕。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后悼枢,我們才把遇到的問題徹底解決代乃。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext跨细、onCreate琳袄、call 等)被系統(tǒng)調(diào)用的順序孕索,我們創(chuàng)建一個簡單的應用茵休,只包含這5個組件嘁捷,不考慮一個應用多進程的情況造成,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。雄嚣。晒屎。(3)]
MainService.java
[圖片上傳中喘蟆。。鼓鲁。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時蕴轨,均已冷啟動的方式啟動應用。
冷啟動骇吭,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk橙弱。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一燥狰,點擊桌面的圖標啟動應用棘脐,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用龙致,其中啟動 MainService 的代碼如下:
[圖片上傳中蛀缝。。目代。(8)]
日志如下:
[圖片上傳中屈梁。。榛了。(9)]
場景三在讶,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中忽冻。真朗。。(10)]
場景四僧诚,其他應用調(diào)用 ContentProvider 的 call 方法啟動遮婶,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中湖笨。旗扑。。(11)]
日志如下:
[圖片上傳中慈省。臀防。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的边败;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行袱衷;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法笑窜,是在 MainApplication 的 onCreate 方法之后執(zhí)行的致燥;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)排截;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎嫌蚤?
為了驗證這個問題辐益,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中脱吱。智政。。(13)]
我們再在上面第四種場景上進行驗證箱蝠,日志如下:
[圖片上傳中续捂。。抡锈。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后疾忍,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎床三?
為了驗證這個問題一罩,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。撇簿。聂渊。(15)]
[圖片上傳中。四瘫。汉嗽。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中找蜜。饼暑。。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行哥捕。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**撰筷。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有畦徘,比如:Application所在類的構造方法毕籽。為了驗證這個問題,將代碼改為:
[圖片上傳中井辆。关筒。。(18)]
程序啟動后杯缺,日志為:
[圖片上傳中平委。。夺谁。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用廉赔。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有匾鸥,自己可以再想想哦蜡塌。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“勿负,那么在項目中為了能”盡早“的提前初始化某些模塊馏艾、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中奴愉。嗯琅摩,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧锭硼,那些“坑”終于還是來了房资。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法檀头。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時轰异,內(nèi)心是崩潰。
所以暑始,如果在 attachBaseContext方法 中要使用 context 的話搭独,那么使用 **this **吧,別再使用 getApplicationContext() 方法了廊镜。下文有分析為什么牙肝。
“坑”二:這個其實不算很坑,也不會引起崩潰嗤朴,但需要注意:
在 Application 的 attachBaseContext方法 中配椭,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次播赁,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate颂郎。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響容为。
做一下驗證乓序,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法坎背,Application 的代碼替劈,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中得滤。陨献。。(20)]
啟動應用后懂更,日志如下:
[圖片上傳中眨业。急膀。。(21)]
可以看到龄捡,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)卓嫂,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個聘殖。

源碼分析

好了晨雳,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍奸腺,那么 為什么 會是這樣呢餐禁?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext突照、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序帮非。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題绷旗,我們知道Java進程的入口方法一般都是在main中喜鼓,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝衔肢,其實應用 main方法 是在 ActivityThread.java 中的庄岖。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎角骤。
a. ActivityThread.java 的 main方法:
[圖片上傳中隅忿。。邦尊。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中金拒。思灌。夭咬。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中着绊。。又沾。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中弊仪。。杖刷。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中励饵。。滑燃。(26)]
f. Application.java 的 attach方法


g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中役听。。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法典予,而這個方法是會調(diào)用到 installProvider方法 中的甜滨,還是在 ActivityThread.java 中:
[圖片上傳中。熙参。艳吠。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。孽椰。。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中凛篙。黍匾。。(31)]
看第二問題呛梆,為什么在我們自定義 Application 中的 attachBaseContext方法 中锐涯,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中填物。纹腌。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中滞磺。升薯。。(33)]
3. mPackageInfo 是什么時候賦值的呢击困?我們從 ContextImpl 實例化的地方入手涎劈,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)阅茶,果不其然蛛枚,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。脸哀。蹦浦。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時撞蜂,就到了注釋b盲镶,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()谅摄,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說徒河,mPackageInfo是LoadedApk的實例),
[圖片上傳中送漠。顽照。。(35)]
[圖片上傳中。代兵。尼酿。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值植影,所以返回的是空裳擎,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值思币。
附圖一張:
[圖片上傳中鹿响。。谷饿。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿惶我,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題博投。文章篇幅不短绸贡,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時毅哗,我們想在應用最早啟動時听怕,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務虑绵。
對其他應用提供服務指的是尿瞭,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider蒸殿,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果筷厘。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況宏所。
剛開始酥艳,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中预侯,但等到測試時發(fā)現(xiàn)了很多意想不到的情況焦人,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”幢痘,但有時候第三方應用能夠使用服務绞幌,而有時候第三方應用不能使等等的問題瘦馍。
于是我們跟蹤代碼没讲,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext沛善、onCreate惠桃、call 等)啟動順序坷剧,跟我們之前理解的稍稍不一樣惰爬。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決惫企。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext撕瞧、onCreate陵叽、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用丛版,只包含這5個組件巩掺,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中页畦。胖替。。(3)]
MainService.java
[圖片上傳中豫缨。独令。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時好芭,均已冷啟動的方式啟動應用记焊。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk栓撞。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一碗硬,點擊桌面的圖標啟動應用瓤湘,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用恩尾,其中啟動 MainService 的代碼如下:
[圖片上傳中弛说。。翰意。(8)]
日志如下:
[圖片上傳中木人。。冀偶。(9)]
場景三醒第,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中进鸠。稠曼。。(10)]
場景四客年,其他應用調(diào)用 ContentProvider 的 call 方法啟動霞幅,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中量瓜。司恳。。(11)]
日志如下:
[圖片上傳中绍傲。扔傅。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行铅鲤;
**3. **Activity划提、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的邢享;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity鹏往、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎骇塘?
為了驗證這個問題伊履,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中款违。唐瀑。。(13)]
我們再在上面第四種場景上進行驗證插爹,日志如下:
[圖片上傳中哄辣。。赠尾。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后力穗,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎气嫁?
為了驗證這個問題当窗,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。寸宵。崖面。(15)]
[圖片上傳中。。。(16)]
我們還在第四個場景下驗證返帕,日志如下:
[圖片上傳中。疏遏。。(17)]
從日志中可以發(fā)現(xiàn)救军,Application 的 onCreate 執(zhí)行時财异,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行唱遭,而是會同時執(zhí)行**戳寸。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有拷泽,比如:Application所在類的構造方法疫鹊。為了驗證這個問題袖瞻,將代碼改為:
[圖片上傳中。拆吆。聋迎。(18)]
程序啟動后,日志為:
[圖片上傳中枣耀。霉晕。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用捞奕。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢牺堰?有,自己可以再想想哦颅围。

遇到的坑

好了伟葫,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊院促、功能或者參數(shù)筏养,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯常拓,一切感覺起來那么美好撼玄,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了墩邀。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法盏浙。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時眉睹,內(nèi)心是崩潰。
所以废膘,如果在 attachBaseContext方法 中要使用 context 的話竹海,那么使用 **this **吧,別再使用 getApplicationContext() 方法了丐黄。下文有分析為什么斋配。
“坑”二:這個其實不算很坑,也不會引起崩潰灌闺,但需要注意:
在 Application 的 attachBaseContext方法 中艰争,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次桂对,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate甩卓。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響蕉斜。
做一下驗證逾柿,在 Application 中調(diào)用 Provider 的 call方法缀棍,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼机错,Provider 的代碼爬范,Activity 的代碼分別如下:
[圖片上傳中。弱匪。青瀑。(20)]
啟動應用后,日志如下:
[圖片上傳中痢法。狱窘。。(21)]
可以看到财搁,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)蘸炸,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個尖奔。

源碼分析

好了搭儒,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍提茁,那么 為什么 會是這樣呢淹禾?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext茴扁、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序铃岔。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題峭火,我們知道Java進程的入口方法一般都是在main中毁习,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝卖丸,其實應用 main方法 是在 ActivityThread.java 中的纺且。
我們查看 ActivityThread.java 的源碼村刨,本文以下的源碼都以
6.0.1_r10
基礎军援。
a. ActivityThread.java 的 main方法:
[圖片上傳中缎罢。泞歉。阿浓。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中趣兄。攀芯。好渠。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中弦撩。裳仆。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中孤钦。歧斟。纯丸。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。静袖。觉鼻。(26)]
f. Application.java 的 attach方法
[圖片上傳中。队橙。坠陈。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。捐康。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法解总,而這個方法是會調(diào)用到 installProvider方法 中的贮匕,還是在 ActivityThread.java 中:
[圖片上傳中。花枫。刻盐。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。劳翰。敦锌。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。佳簸。乙墙。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中生均,調(diào)用 getApplicationContext() 為 null 呢伶丐?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:


2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。疯特。。(33)]
3. mPackageInfo 是什么時候賦值的呢肛走?我們從 ContextImpl 實例化的地方入手漓雅,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)朽色,果不其然邻吞,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。葫男。抱冷。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時梢褐,就到了注釋b旺遮,而此時mPackageInfo是不為空的赵讯,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說耿眉,mPackageInfo是LoadedApk的實例)边翼,
[圖片上傳中。鸣剪。组底。(35)]
[圖片上傳中。筐骇。债鸡。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值铛纬,所以返回的是空厌均,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值饺鹃。
附圖一張:
[圖片上傳中莫秆。。悔详。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿镊屎,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題茄螃。文章篇幅不短缝驳,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時归苍,我們想在應用最早啟動時用狱,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務拼弃。
對其他應用提供服務指的是夏伊,我們的應用中有 ContentProvider闹获,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider铜靶,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時敏弃,我們的應用存在啟動和未啟動的兩種情況盯孙。
剛開始鲁森,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況振惰,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”歌溉,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題骑晶。
于是我們跟蹤代碼痛垛,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext草慧、onCreate、call 等)啟動順序榜晦,跟我們之前理解的稍稍不一樣冠蒋。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決乾胶。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext抖剿、onCreate、call 等)被系統(tǒng)調(diào)用的順序识窿,我們創(chuàng)建一個簡單的應用斩郎,只包含這5個組件,不考慮一個應用多進程的情況喻频,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中缩宜。。甥温。(3)]
MainService.java
[圖片上傳中锻煌。。姻蚓。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時宋梧,均已冷啟動的方式啟動應用。
冷啟動狰挡,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk捂龄。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一加叁,點擊桌面的圖標啟動應用倦沧,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用它匕,其中啟動 MainService 的代碼如下:
[圖片上傳中展融。。豫柬。(8)]
日志如下:
[圖片上傳中告希。。轮傍。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動首装,日志如下:

場景四创夜,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中仙逻,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中驰吓。涧尿。。(11)]
日志如下:
[圖片上傳中檬贰。姑廉。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的翁涤;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行桥言;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法葵礼,是在 MainApplication 的 onCreate 方法之后執(zhí)行的号阿;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)鸳粉;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎扔涧?
為了驗證這個問題,MainApplication 的代碼不變届谈,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中枯夜。。艰山。(13)]
我們再在上面第四種場景上進行驗證湖雹,日志如下:
[圖片上傳中。程剥。劝枣。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的织鲸。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎舔腾?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中搂擦。稳诚。。(15)]
[圖片上傳中。家破。琳省。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中氨距。。棘劣。(17)]
從日志中可以發(fā)現(xiàn)俏让,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行首昔,而是會同時執(zhí)行**寡喝。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有勒奇,比如:Application所在類的構造方法预鬓。為了驗證這個問題,將代碼改為:
[圖片上傳中赊颠。格二。。(18)]
程序啟動后巨税,日志為:
[圖片上傳中蟋定。。草添。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用驶兜。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有远寸,自己可以再想想哦抄淑。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“驰后,那么在項目中為了能”盡早“的提前初始化某些模塊肆资、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中灶芝。嗯郑原,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧夜涕,那些“坑”終于還是來了犯犁。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法女器。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時酸役,內(nèi)心是崩潰。
所以驾胆,如果在 attachBaseContext方法 中要使用 context 的話涣澡,那么使用 **this **吧,別再使用 getApplicationContext() 方法了丧诺。下文有分析為什么入桂。
“坑”二:這個其實不算很坑,也不會引起崩潰驳阎,但需要注意:
在 Application 的 attachBaseContext方法 中抗愁,去調(diào)用自身的 ContentProvider惕艳,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate驹愚。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響劣纲。
做一下驗證逢捺,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法癞季,Application 的代碼劫瞳,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中绷柒。志于。。(20)]
啟動應用后废睦,日志如下:
[圖片上傳中伺绽。。嗜湃。(21)]
可以看到奈应,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類购披,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個杖挣。

源碼分析

好了,現(xiàn)象刚陡、問題和“坑”都經(jīng)歷了一遍惩妇,那么 為什么 會是這樣呢?
我們通過看源碼筐乳,來跟蹤:
1. Application 的 attachBaseContext歌殃、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null哥童。
先看第一個問題挺份,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建贮懈,已經(jīng)幫我們進行封裝匀泊,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼朵你,本文以下的源碼都以
6.0.1_r10
基礎燃乍。
a. ActivityThread.java 的 main方法:
[圖片上傳中墨技。。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。歇式。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。搞监。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中镰矿。琐驴。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中秤标。绝淡。。(26)]
f. Application.java 的 attach方法
[圖片上傳中苍姜。牢酵。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中衙猪。馍乙。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法垫释,而這個方法是會調(diào)用到 installProvider方法 中的潘拨,還是在 ActivityThread.java 中:
[圖片上傳中。饶号。铁追。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。茫船。琅束。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。算谈。涩禀。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中然眼,調(diào)用 getApplicationContext() 為 null 呢艾船?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。高每。屿岂。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。鲸匿。爷怀。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手带欢,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼运授,跟進代碼發(fā)現(xiàn)烤惊,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中吁朦。柒室。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)逗宜,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時伦泥,就到了注釋b,而此時mPackageInfo是不為空的锦溪,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說府怯,mPackageInfo是LoadedApk的實例)刻诊,
[圖片上傳中。牺丙。则涯。(35)]
[圖片上傳中。冲簿。粟判。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值峦剔,所以返回的是空档礁,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值吝沫。
附圖一張:
[圖片上傳中呻澜。。惨险。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿羹幸,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題辫愉。文章篇幅不短栅受,希望能對大家有所幫助恭朗。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時屏镊,我們想在應用最早啟動時,先做一些判斷弟翘,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是,我們的應用中有 ContentProvider厂置,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況凡辱。
剛開始客情,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中愉耙,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”搞坝,但有時候第三方應用能夠使用服務旱爆,而有時候第三方應用不能使等等的問題框冀。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext刹衫、onCreate叽躯、call 等)啟動順序,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決族操。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate兵志、call 等)被系統(tǒng)調(diào)用的順序今阳,我們創(chuàng)建一個簡單的應用洼滚,只包含這5個組件砂碉,不考慮一個應用多進程的情況悼潭,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中颗祝。。组哩。(3)]
MainService.java
[圖片上傳中处渣。。蛛砰。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時罐栈,均已冷啟動的方式啟動應用。
冷啟動泥畅,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk悠瞬。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一涯捻,點擊桌面的圖標啟動應用浅妆,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用障癌,其中啟動 MainService 的代碼如下:
[圖片上傳中凌外。。涛浙。(8)]
日志如下:
[圖片上傳中康辑。。轿亮。(9)]
場景三疮薇,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中我注。按咒。。(10)]
場景四但骨,其他應用調(diào)用 ContentProvider 的 call 方法啟動励七,其中,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中奔缠。掠抬。。(11)]
日志如下:
[圖片上傳中校哎。两波。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的闷哆;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行腰奋;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法阳准,是在 MainApplication 的 onCreate 方法之后執(zhí)行的氛堕;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity馏臭、Service 等的 onCreate(Activity 和 Service 不分先后)野蝇;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎讼稚?
為了驗證這個問題,MainApplication 的代碼不變绕沈,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中锐想。。凤薛。(13)]
我們再在上面第四種場景上進行驗證姓建,日志如下:
[圖片上傳中。缤苫。速兔。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的活玲。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎涣狗?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中舒憾。镀钓。。(15)]
[圖片上傳中镀迂。掸宛。祖搓。(16)]
我們還在第四個場景下驗證呀非,日志如下:
[圖片上傳中。缨该。别凤。(17)]
從日志中可以發(fā)現(xiàn)饰序,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行规哪。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行求豫,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎诉稍?
有蝠嘉,比如:Application所在類的構造方法。為了驗證這個問題杯巨,將代碼改為:
[圖片上傳中蚤告。。服爷。(18)]
程序啟動后杜恰,日志為:
[圖片上傳中。仍源。心褐。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢笼踩?有逗爹,自己可以再想想哦。

遇到的坑

好了嚎于,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“掘而,那么在項目中為了能”盡早“的提前初始化某些模塊挟冠、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中镣屹。嗯圃郊,一切感覺起來那么美好价涝,直到你運行程序崩潰時...
好吧好吧女蜈,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中色瘩,使用了 getApplicationContext方法伪窖。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰居兆。
所以覆山,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧泥栖,別再使用 getApplicationContext() 方法了簇宽。下文有分析為什么。
“坑”二:這個其實不算很坑吧享,也不會引起崩潰魏割,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider钢颂,那么這個 ContentProvider 會被初始化兩次钞它,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯殊鞭,那么一定要檢查是否會有影響遭垛。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法操灿,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法,Application 的代碼,Provider 的代碼芍秆,Activity 的代碼分別如下:
[圖片上傳中惯疙。。妖啥。(20)]
啟動應用后霉颠,日志如下:
[圖片上傳中。迹栓。掉分。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)克伊,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類酥郭,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了愿吹,現(xiàn)象不从、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢犁跪?
我們通過看源碼椿息,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序坷衍。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null寝优。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中枫耳,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建乏矾,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼钻心,本文以下的源碼都以
6.0.1_r10
基礎凄硼。
a. ActivityThread.java 的 main方法:
[圖片上傳中。。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中粗俱。。落追。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中婉刀。吟温。序仙。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。鲁豪。潘悼。(26)]
f. Application.java 的 attach方法
[圖片上傳中。爬橡。治唤。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。糙申。宾添。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的柜裸,還是在 ActivityThread.java 中:
[圖片上傳中缕陕。。疙挺。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中扛邑。。铐然。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中蔬崩。。搀暑。(31)]
看第二問題沥阳,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢自点?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中桐罕。。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中冈绊。侠鳄。。(33)]
3. mPackageInfo 是什么時候賦值的呢死宣?我們從 ContextImpl 實例化的地方入手伟恶,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)毅该,果不其然博秫,看到了 mPackageInfo 被賦值的地方:


4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時眶掌,就到了注釋b挡育,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()朴爬,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說即寒,mPackageInfo是LoadedApk的實例),
[圖片上傳中召噩。母赵。。(35)]
[圖片上傳中具滴。凹嘲。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時构韵, mApplication 還沒有被賦值周蹭,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后疲恢,mApplication 才會被賦值凶朗。
附圖一張:
[圖片上傳中。冈闭。俱尼。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果萎攒,并且深入源碼去分析遇到的問題遇八。文章篇幅不短,希望能對大家有所幫助耍休。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時刃永,我們想在應用最早啟動時,先做一些判斷羊精,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務斯够。
對其他應用提供服務指的是,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider读规,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果抓督。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況束亏。
剛開始铃在,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況碍遍,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”定铜,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題怕敬。
于是我們跟蹤代碼靠闭,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext柬唯、onCreate篓跛、call 等)啟動順序鸣哀,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后越庇,我們才把遇到的問題徹底解決罩锐。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext奉狈、onCreate卤唉、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用仁期,只包含這5個組件桑驱,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中跛蛋。熬的。。(3)]
MainService.java

MainReceiver.java

MainProvider.java

在以下幾個場景測試時赊级,均已冷啟動的方式啟動應用押框。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk理逊。
注意在測試的手機上橡伞,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用晋被,日志如下:

場景二兑徘,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中羡洛。挂脑。。(8)]
日志如下:
[圖片上傳中。崭闲。肋联。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動刁俭,日志如下:
[圖片上傳中牺蹄。。薄翅。(10)]
場景四沙兰,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中翘魄,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中鼎天。。暑竟。(11)]
日志如下:
[圖片上傳中斋射。。但荤。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的罗岖;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity腹躁、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法桑包,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity纺非、Service 等的 onCreate(Activity 和 Service 不分先后)哑了;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題烧颖,MainApplication 的代碼不變弱左,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。炕淮。拆火。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中涂圆。们镜。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后乘综,才會執(zhí)行 Application 的 onCreate 的憎账。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題卡辰,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中胞皱。邪意。。(15)]
[圖片上傳中反砌。雾鬼。。(16)]
我們還在第四個場景下驗證宴树,日志如下:
[圖片上傳中策菜。。酒贬。(17)]
從日志中可以發(fā)現(xiàn)又憨,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行锭吨。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行蠢莺,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎零如?
有躏将,比如:Application所在類的構造方法骂删。為了驗證這個問題啼辣,將代碼改為:
[圖片上傳中腊状。接剩。。(18)]
程序啟動后滔迈,日志為:
[圖片上傳中沿盅。蹂风。喜命。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用沟沙。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有壁榕,自己可以再想想哦。

遇到的坑

好了赎瞎,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“牌里,那么在項目中為了能”盡早“的提前初始化某些模塊、功能或者參數(shù)务甥,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中牡辽。嗯,一切感覺起來那么美好敞临,直到你運行程序崩潰時...
好吧好吧态辛,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中挺尿,使用了 getApplicationContext方法奏黑。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時炊邦,內(nèi)心是崩潰。
所以熟史,如果在 attachBaseContext方法 中要使用 context 的話馁害,那么使用 **this **吧,別再使用 getApplicationContext() 方法了蹂匹。下文有分析為什么碘菜。
“坑”二:這個其實不算很坑,也不會引起崩潰限寞,但需要注意:
在 Application 的 attachBaseContext方法 中忍啸,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次履植,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate吊骤。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響静尼。
做一下驗證白粉,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法鼠渺,Application 的代碼鸭巴,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中拦盹。鹃祖。。(20)]
啟動應用后普舆,日志如下:
[圖片上傳中恬口。。沼侣。(21)]
可以看到祖能,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類蛾洛,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個养铸。

源碼分析

好了,現(xiàn)象轧膘、問題和“坑”都經(jīng)歷了一遍钞螟,那么 為什么 會是這樣呢?
我們通過看源碼谎碍,來跟蹤:
1. Application 的 attachBaseContext鳞滨、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null蟆淀。
先看第一個問題拯啦,我們知道Java進程的入口方法一般都是在main中澡匪,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝提岔,其實應用 main方法 是在 ActivityThread.java 中的仙蛉。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎碱蒙。
a. ActivityThread.java 的 main方法:
[圖片上傳中荠瘪。。赛惩。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中哀墓。。喷兼。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中篮绰。。季惯。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中吠各。。勉抓。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中贾漏。狐榔。髓绽。(26)]
f. Application.java 的 attach方法
[圖片上傳中。人芽。隐圾。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中伍掀。。暇藏。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法蜜笤,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中叨咖。瘩例。。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中甸各。。焰坪。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中趣倾。。某饰。(31)]
看第二問題儒恋,為什么在我們自定義 Application 中的 attachBaseContext方法 中善绎,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中诫尽。禀酱。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中牧嫉。剂跟。。(33)]
3. mPackageInfo 是什么時候賦值的呢酣藻?我們從 ContextImpl 實例化的地方入手曹洽,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)辽剧,果不其然送淆,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中阐斜。智听。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)渡紫,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時到推,就到了注釋b八孝,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說丰榴,mPackageInfo是LoadedApk的實例)鲜滩,
[圖片上傳中业簿。。。(35)]
[圖片上傳中。裙士。融击。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時如绸, mApplication 還沒有被賦值,所以返回的是空杯瞻,只有把 attachBaseContext方法 執(zhí)行完成后秽梅,mApplication 才會被賦值辛友。
附圖一張:
[圖片上傳中墨礁。风科。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題傻盟。文章篇幅不短,希望能對大家有所幫助莹规。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時括荡,我們想在應用最早啟動時褪子,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務深胳。
對其他應用提供服務指的是拓春,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider征绎,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果胀茵。當?shù)谌綉谜{(diào)用我們的應用時玉组,我們的應用存在啟動和未啟動的兩種情況往史。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中抒和,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”幸缕,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼踱阿,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate吴趴、call 等)啟動順序颤殴,跟我們之前理解的稍稍不一樣写妥。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決迎吵。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate、call 等)被系統(tǒng)調(diào)用的順序磅崭,我們創(chuàng)建一個簡單的應用癌蓖,只包含這5個組件,不考慮一個應用多進程的情況慈迈,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中锌妻。恼五。圆米。(3)]
MainService.java
[圖片上傳中。咏尝。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時搞糕,均已冷啟動的方式啟動應用。
冷啟動坝辫,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk妖泄。
注意在測試的手機上医窿,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用树肃,其中啟動 MainService 的代碼如下:
[圖片上傳中褒侧。攒射。挺庞。(8)]
日志如下:
[圖片上傳中。。夯缺。(9)]
場景三鹿鳖,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中。。赋元。(10)]
場景四忘蟹,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中搁凸,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中媚值。。护糖。(11)]
日志如下:
[圖片上傳中褥芒。。嫡良。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的锰扶;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity寝受、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法坷牛,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity很澄、Service 等的 onCreate(Activity 和 Service 不分先后)京闰;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題甩苛,MainApplication 的代碼不變蹂楣,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。浪藻。捐迫。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中爱葵。施戴。反浓。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的赞哗。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎雷则?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中肪笋。月劈。。(15)]
[圖片上傳中藤乙。猜揪。。(16)]
我們還在第四個場景下驗證坛梁,日志如下:


從日志中可以發(fā)現(xiàn)而姐,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行划咐。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行拴念,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎褐缠?
有政鼠,比如:Application所在類的構造方法。為了驗證這個問題队魏,將代碼改為:
[圖片上傳中公般。。器躏。(18)]
程序啟動后俐载,日志為:
[圖片上傳中蟹略。登失。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用挖炬。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢揽浙?有,自己可以再想想哦。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“旱物,那么在項目中為了能”盡早“的提前初始化某些模塊骂铁、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中裆针。嗯,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧敞曹,那些“坑”終于還是來了账月。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法澳迫。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時局齿,內(nèi)心是崩潰。
所以橄登,如果在 attachBaseContext方法 中要使用 context 的話抓歼,那么使用 **this **吧,別再使用 getApplicationContext() 方法了拢锹。下文有分析為什么谣妻。
“坑”二:這個其實不算很坑,也不會引起崩潰卒稳,但需要注意:
在 Application 的 attachBaseContext方法 中拌禾,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次展哭,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate湃窍。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響匪傍。
做一下驗證您市,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法役衡,Application 的代碼茵休,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中手蝎。榕莺。。(20)]
啟動應用后棵介,日志如下:
[圖片上傳中钉鸯。。邮辽。(21)]
可以看到唠雕,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類吨述,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個岩睁。

源碼分析

好了,現(xiàn)象揣云、問題和“坑”都經(jīng)歷了一遍捕儒,那么 為什么 會是這樣呢?
我們通過看源碼邓夕,來跟蹤:
1. Application 的 attachBaseContext刘莹、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序亿笤。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題栋猖,我們知道Java進程的入口方法一般都是在main中净薛,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝蒲拉,其實應用 main方法 是在 ActivityThread.java 中的肃拜。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎雌团。
a. ActivityThread.java 的 main方法:
[圖片上傳中燃领。。锦援。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中猛蔽。。灵寺。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中曼库。。略板。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中毁枯。。叮称。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中种玛。。瓤檐。(26)]
f. Application.java 的 attach方法
[圖片上傳中赂韵。。挠蛉。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中祭示。。碌秸。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法绍移,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中讥电。。轧抗。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中恩敌。。横媚。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中纠炮。。。(31)]
看第二問題柔袁,為什么在我們自定義 Application 中的 attachBaseContext方法 中差购,調(diào)用 getApplicationContext() 為 null 呢?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中耕肩。因妇。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中猿诸。婚被。。(33)]
3. mPackageInfo 是什么時候賦值的呢梳虽?我們從 ContextImpl 實例化的地方入手址芯,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)窜觉,果不其然谷炸,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。禀挫。淑廊。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時特咆,就到了注釋b季惩,而此時mPackageInfo是不為空的,所以會執(zhí)行mPackageInfo.getApplication()腻格,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說画拾,mPackageInfo是LoadedApk的實例),
[圖片上傳中菜职。青抛。。(35)]
[圖片上傳中酬核。蜜另。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時嫡意, mApplication 還沒有被賦值举瑰,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后蔬螟,mApplication 才會被賦值此迅。
附圖一張:
[圖片上傳中。。耸序。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿忍些,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題坎怪。文章篇幅不短罢坝,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時搅窿,我們想在應用最早啟動時嘁酿,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務戈钢。
對其他應用提供服務指的是痹仙,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider殉了,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果开仰。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況薪铜。
剛開始众弓,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況隔箍,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”谓娃,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題蜒滩。
于是我們跟蹤代碼滨达,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate俯艰、call 等)啟動順序捡遍,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后竹握,我們才把遇到的問題徹底解決画株。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate啦辐、call 等)被系統(tǒng)調(diào)用的順序谓传,我們創(chuàng)建一個簡單的應用,只包含這5個組件芹关,不考慮一個應用多進程的情況续挟,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中。充边。庸推。(3)]
MainService.java
[圖片上傳中常侦。浇冰。贬媒。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時,均已冷啟動的方式啟動應用肘习。
冷啟動际乘,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上漂佩,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一脖含,點擊桌面的圖標啟動應用,日志如下:

場景二投蝉,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中。嚎莉。优烧。(8)]
日志如下:
[圖片上傳中。庸娱。着绊。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動熟尉,日志如下:
[圖片上傳中归露。。斤儿。(10)]
場景四剧包,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中往果,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中疆液。。棚放。(11)]
日志如下:
[圖片上傳中枚粘。。飘蚯。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的馍迄;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity局骤、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法攀圈,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity峦甩、Service 等的 onCreate(Activity 和 Service 不分先后)赘来;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎现喳?
為了驗證這個問題,MainApplication 的代碼不變犬辰,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中嗦篱。。幌缝。(13)]
我們再在上面第四種場景上進行驗證灸促,日志如下:
[圖片上傳中。涵卵。浴栽。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的轿偎。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎典鸡?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中坏晦。萝玷。。(15)]
[圖片上傳中英遭。间护。。(16)]
我們還在第四個場景下驗證挖诸,日志如下:
[圖片上傳中汁尺。。多律。(17)]
從日志中可以發(fā)現(xiàn)痴突,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行狼荞。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行辽装,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎相味?
有拾积,比如:Application所在類的構造方法。為了驗證這個問題丰涉,將代碼改為:


程序啟動后拓巧,日志為:
[圖片上傳中。一死。肛度。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢投慈?有承耿,自己可以再想想哦冠骄。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“加袋,那么在項目中為了能”盡早“的提前初始化某些模塊凛辣、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中锁荔。嗯蟀给,一切感覺起來那么美好蝙砌,直到你運行程序崩潰時...
好吧好吧阳堕,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中择克,使用了 getApplicationContext方法恬总。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰肚邢。
所以壹堰,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧骡湖,別再使用 getApplicationContext() 方法了贱纠。下文有分析為什么。
“坑”二:這個其實不算很坑响蕴,也不會引起崩潰弛姜,但需要注意:
在 Application 的 attachBaseContext方法 中方淤,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate初肉。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響疙咸。
做一下驗證陵叽,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法肥缔,Application 的代碼莲兢,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中续膳。改艇。。(20)]
啟動應用后姑宽,日志如下:
[圖片上傳中遣耍。。炮车。(21)]
可以看到舵变,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)酣溃,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個纪隙。

源碼分析

好了赊豌,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍绵咱,那么 為什么 會是這樣呢碘饼?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext悲伶、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序艾恼。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題麸锉,我們知道Java進程的入口方法一般都是在main中钠绍,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝花沉,其實應用 main方法 是在 ActivityThread.java 中的柳爽。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎碱屁。
a. ActivityThread.java 的 main方法:
[圖片上傳中磷脯。。娩脾。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中赵誓。。晦雨。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中架曹。。闹瞧。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中绑雄。。奥邮。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中万牺。。洽腺。(26)]
f. Application.java 的 attach方法
[圖片上傳中脚粟。。蘸朋。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中核无。。藕坯。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法团南,而這個方法是會調(diào)用到 installProvider方法 中的噪沙,還是在 ActivityThread.java 中:
[圖片上傳中。吐根。正歼。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。拷橘。局义。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。冗疮。萄唇。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中赌厅,調(diào)用 getApplicationContext() 為 null 呢穷绵?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。特愿。。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中勾缭。揍障。。(33)]
3. mPackageInfo 是什么時候賦值的呢俩由?我們從 ContextImpl 實例化的地方入手毒嫡,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼,跟進代碼發(fā)現(xiàn)幻梯,果不其然兜畸,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中。碘梢。咬摇。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時瓦堵,就到了注釋b存和,而此時mPackageInfo是不為空的飞涂,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說在扰,mPackageInfo是LoadedApk的實例),
[圖片上傳中雷客。芒珠。。(35)]
[圖片上傳中搅裙。皱卓。总放。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值好爬,所以返回的是空局雄,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值存炮。
附圖一張:
[圖片上傳中炬搭。。穆桂。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿宫盔,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題享完。文章篇幅不短灼芭,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時般又,我們想在應用最早啟動時彼绷,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務茴迁。
對其他應用提供服務指的是寄悯,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider堕义,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果猜旬。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況倦卖。
剛開始洒擦,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況怕膛,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”熟嫩,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題嘉竟。
于是我們跟蹤代碼邦危,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext、onCreate舍扰、call 等)啟動順序倦蚪,跟我們之前理解的稍稍不一樣。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后边苹,我們才把遇到的問題徹底解決陵且。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext、onCreate、call 等)被系統(tǒng)調(diào)用的順序慕购,我們創(chuàng)建一個簡單的應用聊疲,只包含這5個組件,不考慮一個應用多進程的情況沪悲,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中获洲。。殿如。(3)]
MainService.java
[圖片上傳中贡珊。。涉馁。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時门岔,均已冷啟動的方式啟動應用。
冷啟動烤送,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk寒随。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一帮坚,點擊桌面的圖標啟動應用妻往,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用叶沛,其中啟動 MainService 的代碼如下:
[圖片上傳中蒲讯。。灰署。(8)]
日志如下:
[圖片上傳中。局嘁。溉箕。(9)]
場景三,應用通過接受開機廣播啟動的方式啟動悦昵,日志如下:
[圖片上傳中肴茄。。但指。(10)]
場景四寡痰,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中棋凳,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中拦坠。。剩岳。(11)]
日志如下:
[圖片上傳中贞滨。。拍棕。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity尸诽、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法抛丽,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity昧旨、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題事甜,MainApplication 的代碼不變,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中示弓。讳侨。。(13)]
我們再在上面第四種場景上進行驗證奏属,日志如下:
[圖片上傳中跨跨。。囱皿。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后勇婴,才會執(zhí)行 Application 的 onCreate 的。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎嘱腥?
為了驗證這個問題耕渴,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中。齿兔。橱脸。(15)]
[圖片上傳中。分苇。添诉。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中医寿。栏赴。。(17)]
從日志中可以發(fā)現(xiàn)靖秩,Application 的 onCreate 執(zhí)行時须眷,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行沟突,而是會同時執(zhí)行**花颗。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有事扭,比如:Application所在類的構造方法捎稚。為了驗證這個問題,將代碼改為:
[圖片上傳中。今野。葡公。(18)]
程序啟動后,日志為:


問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用条霜。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢催什?有,自己可以再想想哦宰睡。

遇到的坑

好了蒲凶,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊拆内、功能或者參數(shù)旋圆,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯麸恍,一切感覺起來那么美好灵巧,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了抹沪。
“坑”一:在 Application 的 attachBaseContext方法 中刻肄,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時融欧,內(nèi)心是崩潰敏弃。
所以,如果在 attachBaseContext方法 中要使用 context 的話噪馏,那么使用 **this **吧麦到,別再使用 getApplicationContext() 方法了。下文有分析為什么欠肾。
“坑”二:這個其實不算很坑隅要,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中董济,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次要门,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate虏肾。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響欢搜。
做一下驗證封豪,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法炒瘟,Application 的代碼吹埠,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中。缘琅。粘都。(20)]
啟動應用后,日志如下:
[圖片上傳中刷袍。翩隧。。(21)]
可以看到呻纹,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)堆生,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個雷酪。

源碼分析

好了淑仆,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍哥力,那么 為什么 會是這樣呢荣瑟?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext肛炮、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序局荚。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題钞澳,我們知道Java進程的入口方法一般都是在main中怠惶,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝轧粟,其實應用 main方法 是在 ActivityThread.java 中的策治。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎兰吟。
a. ActivityThread.java 的 main方法:
[圖片上傳中通惫。。混蔼。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中履腋。。惭嚣。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中遵湖。。晚吞。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中延旧。。槽地。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中迁沫。芦瘾。。(26)]
f. Application.java 的 attach方法
[圖片上傳中集畅。近弟。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中牡整。藐吮。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法逃贝,而這個方法是會調(diào)用到 installProvider方法 中的谣辞,還是在 ActivityThread.java 中:
[圖片上傳中。沐扳。泥从。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。沪摄。躯嫉。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。杨拐。祈餐。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中哄陶,調(diào)用 getApplicationContext() 為 null 呢帆阳?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。屋吨。蜒谤。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。至扰。鳍徽。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手敢课,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼阶祭,跟進代碼發(fā)現(xiàn),果不其然直秆,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中胖翰。。切厘。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法),在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時懊缺,就到了注釋b疫稿,而此時mPackageInfo是不為空的培他,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說遗座,mPackageInfo是LoadedApk的實例)舀凛,
[圖片上傳中。途蒋。猛遍。(35)]
[圖片上傳中。号坡。懊烤。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值宽堆,所以返回的是空腌紧,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值畜隶。
附圖一張:
[圖片上傳中壁肋。。籽慢。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿浸遗,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題箱亿。文章篇幅不短跛锌,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時极景,我們想在應用最早啟動時察净,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是介粘,我們的應用中有 ContentProvider玄帕,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果译秦。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況击碗。
剛開始筑悴,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中,但等到測試時發(fā)現(xiàn)了很多意想不到的情況稍途,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”阁吝,但有時候第三方應用能夠使用服務,而有時候第三方應用不能使等等的問題械拍。
于是我們跟蹤代碼突勇,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext装盯、onCreate、call 等)啟動順序甲馋,跟我們之前理解的稍稍不一樣埂奈。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決定躏。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext账磺、onCreate、call 等)被系統(tǒng)調(diào)用的順序痊远,我們創(chuàng)建一個簡單的應用垮抗,只包含這5個組件,不考慮一個應用多進程的情況拗引,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中借宵。。矾削。(3)]
MainService.java
[圖片上傳中壤玫。。哼凯。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時欲间,均已冷啟動的方式啟動應用。
冷啟動断部,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk猎贴。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一蝴光,點擊桌面的圖標啟動應用她渴,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用蔑祟,其中啟動 MainService 的代碼如下:
[圖片上傳中趁耗。。疆虚。(8)]
日志如下:
[圖片上傳中苛败。。径簿。(9)]
場景三罢屈,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中篇亭。缠捌。。(10)]
場景四译蒂,其他應用調(diào)用 ContentProvider 的 call 方法啟動鄙币,其中肃叶,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。十嘿。。(11)]
日志如下:
[圖片上傳中岳锁。绩衷。。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的激率;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行咳燕;
**3. **Activity、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法乒躺,是在 MainApplication 的 onCreate 方法之后執(zhí)行的招盲;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先后)嘉冒;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎曹货?
為了驗證這個問題,MainApplication 的代碼不變讳推,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中顶籽。。银觅。(13)]
我們再在上面第四種場景上進行驗證礼饱,日志如下:
[圖片上傳中。究驴。镊绪。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的洒忧。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎蝴韭?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中跑慕。万皿。。(15)]
[圖片上傳中核行。牢硅。。(16)]
我們還在第四個場景下驗證芝雪,日志如下:
[圖片上傳中减余。。惩系。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行逊彭。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行吼砂,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎杜跷?
有,比如:Application所在類的構造方法。為了驗證這個問題妖胀,將代碼改為:
[圖片上傳中。惠勒。赚抡。(18)]
程序啟動后,日志為:
[圖片上傳中纠屋。涂臣。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用售担。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢赁遗?有,自己可以再想想哦灼舍。

遇到的坑

好了吼和,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊骑素、功能或者參數(shù)炫乓,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯献丑,一切感覺起來那么美好末捣,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了创橄。
“坑”一:在 Application 的 attachBaseContext方法 中箩做,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時妥畏,內(nèi)心是崩潰邦邦。
所以,如果在 attachBaseContext方法 中要使用 context 的話醉蚁,那么使用 **this **吧燃辖,別再使用 getApplicationContext() 方法了。下文有分析為什么网棍。
“坑”二:這個其實不算很坑黔龟,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider氏身,那么這個 ContentProvider 會被初始化兩次巍棱,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯蛋欣,那么一定要檢查是否會有影響航徙。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法陷虎,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法捉偏,Application 的代碼,Provider 的代碼泻红,Activity 的代碼分別如下:


啟動應用后,日志如下:
[圖片上傳中霞掺。谊路。。(21)]
可以看到菩彬,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)缠劝,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個骗灶。

源碼分析

好了惨恭,現(xiàn)象、問題和“坑”都經(jīng)歷了一遍耙旦,那么 為什么 會是這樣呢脱羡?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext免都、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序锉罐。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題绕娘,我們知道Java進程的入口方法一般都是在main中脓规,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝险领,其實應用 main方法 是在 ActivityThread.java 中的侨舆。
我們查看 ActivityThread.java 的源碼,本文以下的源碼都以
6.0.1_r10
基礎绢陌。
a. ActivityThread.java 的 main方法:
[圖片上傳中挨下。。下面。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中复颈。。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中耗啦。凿菩。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中帜讲。衅谷。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中似将。获黔。。(26)]
f. Application.java 的 attach方法
[圖片上傳中在验。玷氏。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中腋舌。盏触。剑令。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法晰筛,而這個方法是會調(diào)用到 installProvider方法 中的,還是在 ActivityThread.java 中:
[圖片上傳中猜谚。授艰。辨嗽。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。淮腾。糟需。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。来破。篮灼。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中徘禁,調(diào)用 getApplicationContext() 為 null 呢诅诱?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。送朱。娘荡。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。驶沼。炮沐。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手回怜,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼大年,跟進代碼發(fā)現(xiàn),果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中翔试。轻要。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)垦缅,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時冲泥,就到了注釋b,而此時mPackageInfo是不為空的壁涎,所以會執(zhí)行mPackageInfo.getApplication()凡恍,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)怔球,
[圖片上傳中嚼酝。。竟坛。(35)]
[圖片上傳中革半。。流码。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值延刘,所以返回的是空漫试,只有把 attachBaseContext方法 執(zhí)行完成后,mApplication 才會被賦值碘赖。
附圖一張:
[圖片上傳中驾荣。。普泡。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿播掷,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果,并且深入源碼去分析遇到的問題撼班。文章篇幅不短歧匈,希望能對大家有所幫助。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時砰嘁,我們想在應用最早啟動時件炉,先做一些判斷,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務矮湘。
對其他應用提供服務指的是斟冕,我們的應用中有 ContentProvider,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider缅阳,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果磕蛇。當?shù)谌綉谜{(diào)用我們的應用時,我們的應用存在啟動和未啟動的兩種情況。
剛開始秀撇,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中超棺,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”捌袜,但有時候第三方應用能夠使用服務说搅,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼虏等,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext弄唧、onCreate、call 等)啟動順序霍衫,跟我們之前理解的稍稍不一樣候引。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決敦跌。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext澄干、onCreate、call 等)被系統(tǒng)調(diào)用的順序柠傍,我們創(chuàng)建一個簡單的應用麸俘,只包含這5個組件,不考慮一個應用多進程的情況惧笛,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中从媚。。患整。(3)]
MainService.java
[圖片上傳中拜效。。各谚。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時紧憾,均已冷啟動的方式啟動應用。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk。
注意在測試的手機上姿染,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用望抽,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用履婉,其中啟動 MainService 的代碼如下:
[圖片上傳中煤篙。。毁腿。(8)]
日志如下:
[圖片上傳中辑奈。苛茂。。(9)]
場景三鸠窗,應用通過接受開機廣播啟動的方式啟動妓羊,日志如下:
[圖片上傳中。稍计。躁绸。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動臣嚣,其中净刮,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。硅则。淹父。(11)]
日志如下:
[圖片上傳中。怎虫。暑认。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行大审;
**3. **Activity蘸际、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的徒扶;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity捡鱼、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎酷愧?
為了驗證這個問題,MainApplication 的代碼不變缠诅,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中溶浴。。管引。(13)]
我們再在上面第四種場景上進行驗證士败,日志如下:
[圖片上傳中。褥伴。谅将。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的重慢。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎饥臂?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中似踱。隅熙。稽煤。(15)]
[圖片上傳中。囚戚。酵熙。(16)]
我們還在第四個場景下驗證,日志如下:
[圖片上傳中驰坊。匾二。。(17)]
從日志中可以發(fā)現(xiàn)拳芙,Application 的 onCreate 執(zhí)行時察藐,ContentProvider 的 call方法 也在同時執(zhí)行。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行态鳖,而是會同時執(zhí)行**转培。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有浆竭,比如:Application所在類的構造方法浸须。為了驗證這個問題,將代碼改為:
[圖片上傳中邦泄。删窒。。(18)]
程序啟動后顺囊,日志為:
[圖片上傳中肌索。。特碳。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用诚亚。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢?有午乓,自己可以再想想哦站宗。

遇到的坑

好了,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“益愈,那么在項目中為了能”盡早“的提前初始化某些模塊梢灭、功能或者參數(shù),那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中蒸其。嗯敏释,一切感覺起來那么美好,直到你運行程序崩潰時...
好吧好吧摸袁,那些“坑”終于還是來了钥顽。
“坑”一:在 Application 的 attachBaseContext方法 中,使用了 getApplicationContext方法靠汁。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時耳鸯,內(nèi)心是崩潰湿蛔。
所以,如果在 attachBaseContext方法 中要使用 context 的話县爬,那么使用 **this **吧阳啥,別再使用 getApplicationContext() 方法了牌废。下文有分析為什么蜜托。
“坑”二:這個其實不算很坑停巷,也不會引起崩潰尊惰,但需要注意:
在 Application 的 attachBaseContext方法 中患亿,去調(diào)用自身的 ContentProvider拙毫,那么這個 ContentProvider 會被初始化兩次蕊爵,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate浊闪。如果你在 ContentProvider 的 onCreate 中有一些邏輯泌枪,那么一定要檢查是否會有影響概荷。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法碌燕,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法误证,Application 的代碼,Provider 的代碼修壕,Activity 的代碼分別如下:
[圖片上傳中愈捅。。慈鸠。(20)]
啟動應用后蓝谨,日志如下:


可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)青团,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類譬巫,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了督笆,現(xiàn)象芦昔、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢胖腾?
我們通過看源碼,來跟蹤:
1. Application 的 attachBaseContext瘪松、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序咸作。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null
先看第一個問題宵睦,我們知道Java進程的入口方法一般都是在main中记罚,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建,已經(jīng)幫我們進行封裝壳嚎,其實應用 main方法 是在 ActivityThread.java 中的桐智。
我們查看 ActivityThread.java 的源碼末早,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中说庭。然磷。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中刊驴。姿搜。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中捆憎。舅柜。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中躲惰。致份。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中础拨。氮块。。(26)]
f. Application.java 的 attach方法
[圖片上傳中太伊。雇锡。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中僚焦。锰提。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法芳悲,而這個方法是會調(diào)用到 installProvider方法 中的立肘,還是在 ActivityThread.java 中:
[圖片上傳中。名扛。谅年。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。肮韧。融蹂。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。弄企。超燃。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中拘领,調(diào)用 getApplicationContext() 為 null 呢意乓?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。约素。届良。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中笆凌。。士葫。(33)]
3. mPackageInfo 是什么時候賦值的呢乞而?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼为障,跟進代碼發(fā)現(xiàn)晦闰,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中鳍怨。呻右。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)鞋喇,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時声滥,就到了注釋b,而此時mPackageInfo是不為空的侦香,所以會執(zhí)行mPackageInfo.getApplication()落塑,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說台颠,mPackageInfo是LoadedApk的實例)厌衙,
[圖片上傳中。赠橙。散吵。(35)]
[圖片上傳中龙考。。矾睦。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時晦款, mApplication 還沒有被賦值,所以返回的是空枚冗,只有把 attachBaseContext方法 執(zhí)行完成后缓溅,mApplication 才會被賦值。
附圖一張:
[圖片上傳中赁温。坛怪。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿股囊,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果袜匿,并且深入源碼去分析遇到的問題。文章篇幅不短毁涉,希望能對大家有所幫助沉帮。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時锈死,我們想在應用最早啟動時贫堰,先做一些判斷穆壕,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是其屏,我們的應用中有 ContentProvider喇勋,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果偎行。當?shù)谌綉谜{(diào)用我們的應用時川背,我們的應用存在啟動和未啟動的兩種情況。
剛開始蛤袒,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中熄云,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”妙真,但有時候第三方應用能夠使用服務缴允,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼珍德,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext练般、onCreate、call 等)啟動順序锈候,跟我們之前理解的稍稍不一樣薄料。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后,我們才把遇到的問題徹底解決泵琳。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext摄职、onCreate、call 等)被系統(tǒng)調(diào)用的順序虑稼,我們創(chuàng)建一個簡單的應用琳钉,只包含這5個組件,不考慮一個應用多進程的情況蛛倦,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中歌懒。。溯壶。(3)]
MainService.java
[圖片上傳中及皂。。且改。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時验烧,均已冷啟動的方式啟動應用。
冷啟動又跛,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk碍拆。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用感混,日志如下:

場景二端幼,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中弧满。婆跑。。(8)]
日志如下:
[圖片上傳中庭呜。滑进。。(9)]
場景三募谎,應用通過接受開機廣播啟動的方式啟動扶关,日志如下:
[圖片上傳中。数冬。驮审。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動吉执,其中疯淫,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。戳玫。熙掺。(11)]
日志如下:
[圖片上傳中。咕宿。币绩。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行府阀;
**3. **Activity缆镣、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的试浙;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity董瞻、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎田巴?
為了驗證這個問題钠糊,MainApplication 的代碼不變君躺,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中篮奄。艺谆。园细。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中懊悯。凛捏。吞瞪。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的岗喉。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎云稚?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中沈堡。。燕雁。(15)]
[圖片上傳中诞丽。。拐格。(16)]
我們還在第四個場景下驗證僧免,日志如下:
[圖片上傳中。捏浊。懂衩。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時金踪,ContentProvider 的 call方法 也在同時執(zhí)行浊洞。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**胡岔。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎法希?
有,比如:Application所在類的構造方法靶瘸。為了驗證這個問題苫亦,將代碼改為:
[圖片上傳中。怨咪。屋剑。(18)]
程序啟動后,日志為:
[圖片上傳中诗眨。唉匾。。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用匠楚。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢肄鸽?有,自己可以再想想哦油啤。

遇到的坑

好了典徘,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊益咬、功能或者參數(shù)逮诲,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中帜平。嗯,一切感覺起來那么美好梅鹦,直到你運行程序崩潰時...
好吧好吧裆甩,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中齐唆,使用了 getApplicationContext方法嗤栓。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰箍邮。
所以茉帅,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧锭弊,別再使用 getApplicationContext() 方法了堪澎。下文有分析為什么。
“坑”二:這個其實不算很坑味滞,也不會引起崩潰樱蛤,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider剑鞍,那么這個 ContentProvider 會被初始化兩次昨凡,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯蚁署,那么一定要檢查是否會有影響土匀。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法形用,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法就轧,Application 的代碼,Provider 的代碼田度,Activity 的代碼分別如下:
[圖片上傳中妒御。。镇饺。(20)]
啟動應用后乎莉,日志如下:
[圖片上傳中。奸笤。惋啃。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)监右,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類边灭,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了健盒,現(xiàn)象绒瘦、問題和“坑”都經(jīng)歷了一遍称簿,那么 為什么 會是這樣呢?
我們通過看源碼惰帽,來跟蹤:
1. Application 的 attachBaseContext憨降、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null该酗。
先看第一個問題授药,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建呜魄,已經(jīng)幫我們進行封裝苦丁,其實應用 main方法 是在 ActivityThread.java 中的鸟雏。
我們查看 ActivityThread.java 的源碼酝静,本文以下的源碼都以
6.0.1_r10
基礎贞绳。
a. ActivityThread.java 的 main方法:
[圖片上傳中膳殷。操骡。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中赚窃。册招。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中勒极。是掰。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中辱匿。键痛。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中匾七。絮短。。(26)]
f. Application.java 的 attach方法
[圖片上傳中昨忆。丁频。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中邑贴。席里。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法拢驾,而這個方法是會調(diào)用到 installProvider方法 中的奖磁,還是在 ActivityThread.java 中:
[圖片上傳中。繁疤。署穗。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中寥裂。。案疲。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中封恰。。褐啡。(31)]
看第二問題诺舔,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢备畦?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中低飒。。懂盐。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中褥赊。。莉恼。(33)]
3. mPackageInfo 是什么時候賦值的呢拌喉?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼俐银,跟進代碼發(fā)現(xiàn)尿背,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中捶惜。田藐。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)吱七,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時汽久,就到了注釋b,而此時mPackageInfo是不為空的踊餐,所以會執(zhí)行mPackageInfo.getApplication()回窘,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)市袖,
[圖片上傳中啡直。。苍碟。(35)]


看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時酒觅, mApplication 還沒有被賦值,所以返回的是空微峰,只有把 attachBaseContext方法 執(zhí)行完成后舷丹,mApplication 才會被賦值。
附圖一張:
[圖片上傳中蜓肆。颜凯。谋币。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果症概,并且深入源碼去分析遇到的問題蕾额。文章篇幅不短,希望能對大家有所幫助彼城。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時诅蝶,我們想在應用最早啟動時,先做一些判斷募壕,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務调炬。
對其他應用提供服務指的是,我們的應用中有 ContentProvider舱馅,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider缰泡,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時代嗤,我們的應用存在啟動和未啟動的兩種情況棘钞。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中资溃,但等到測試時發(fā)現(xiàn)了很多意想不到的情況武翎,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”烈炭,但有時候第三方應用能夠使用服務溶锭,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼符隙,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext趴捅、onCreate、call 等)啟動順序缚俏,跟我們之前理解的稍稍不一樣祠够。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后掘譬,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext猎拨、onCreate、call 等)被系統(tǒng)調(diào)用的順序屠阻,我們創(chuàng)建一個簡單的應用红省,只包含這5個組件,不考慮一個應用多進程的情況国觉,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中吧恃。。麻诀。(3)]
MainService.java
[圖片上傳中痕寓。傲醉。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時呻率,均已冷啟動的方式啟動應用硬毕。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk筷凤。
注意在測試的手機上昭殉,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用藐守,日志如下:

場景二挪丢,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中卢厂。乾蓬。。(8)]
日志如下:
[圖片上傳中慎恒。任内。。(9)]
場景三融柬,應用通過接受開機廣播啟動的方式啟動死嗦,日志如下:
[圖片上傳中。粒氧。越除。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動外盯,其中摘盆,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。饱苟。孩擂。(11)]
日志如下:
[圖片上傳中。箱熬。类垦。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行城须;
**3. **Activity蚤认、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之后執(zhí)行的酿傍;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity烙懦、Service 等的 onCreate(Activity 和 Service 不分先后);

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題氯析,MainApplication 的代碼不變亏较,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。掩缓。雪情。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中你辣。巡通。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后舍哄,才會執(zhí)行 Application 的 onCreate 的宴凉。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題表悬,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中弥锄。。蟆沫。(15)]
[圖片上傳中籽暇。。饭庞。(16)]
我們還在第四個場景下驗證戒悠,日志如下:
[圖片上傳中。舟山。绸狐。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時捏顺,ContentProvider 的 call方法 也在同時執(zhí)行六孵。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行纬黎,而是會同時執(zhí)行**幅骄。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎?
有本今,比如:Application所在類的構造方法拆座。為了驗證這個問題,將代碼改為:
[圖片上傳中冠息。挪凑。。(18)]
程序啟動后逛艰,日志為:
[圖片上傳中躏碳。。散怖。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用菇绵。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢肄渗?有,自己可以再想想哦咬最。

遇到的坑

好了翎嫡,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“顷蟀,那么在項目中為了能”盡早“的提前初始化某些模塊膝迎、功能或者參數(shù)敛腌,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中衡怀。嗯澄惊,一切感覺起來那么美好撒穷,直到你運行程序崩潰時...
好吧好吧捕儒,那些“坑”終于還是來了隐圾。
“坑”一:在 Application 的 attachBaseContext方法 中望几,使用了 getApplicationContext方法碗脊。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時,內(nèi)心是崩潰橄妆。
所以衙伶,如果在 attachBaseContext方法 中要使用 context 的話,那么使用 **this **吧害碾,別再使用 getApplicationContext() 方法了矢劲。下文有分析為什么。
“坑”二:這個其實不算很坑慌随,也不會引起崩潰芬沉,但需要注意:
在 Application 的 attachBaseContext方法 中,去調(diào)用自身的 ContentProvider阁猜,那么這個 ContentProvider 會被初始化兩次丸逸,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate。如果你在 ContentProvider 的 onCreate 中有一些邏輯剃袍,那么一定要檢查是否會有影響黄刚。
做一下驗證,在 Application 中調(diào)用 Provider 的 call方法民效,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法憔维,Application 的代碼,Provider 的代碼畏邢,Activity 的代碼分別如下:
[圖片上傳中业扒。。舒萎。(20)]
啟動應用后程储,日志如下:
[圖片上傳中。。章鲤。(21)]
可以看到致板,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類咏窿,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個斟或。

源碼分析

好了,現(xiàn)象集嵌、問題和“坑”都經(jīng)歷了一遍萝挤,那么 為什么 會是這樣呢?
我們通過看源碼根欧,來跟蹤:
1. Application 的 attachBaseContext怜珍、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null凤粗。
先看第一個問題酥泛,我們知道Java進程的入口方法一般都是在main中,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建嫌拣,已經(jīng)幫我們進行封裝柔袁,其實應用 main方法 是在 ActivityThread.java 中的。
我們查看 ActivityThread.java 的源碼异逐,本文以下的源碼都以
6.0.1_r10
基礎捶索。
a. ActivityThread.java 的 main方法:
[圖片上傳中。灰瞻。腥例。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中。酝润。燎竖。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中。要销。构回。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。蕉陋。捐凭。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中拨扶。凳鬓。。(26)]
f. Application.java 的 attach方法
[圖片上傳中患民。缩举。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。仅孩。托猩。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的辽慕,還是在 ActivityThread.java 中:
[圖片上傳中京腥。。溅蛉。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中公浪。。船侧。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中欠气。。镜撩。(31)]
看第二問題预柒,為什么在我們自定義 Application 中的 attachBaseContext方法 中,調(diào)用 getApplicationContext() 為 null 呢袁梗?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中宜鸯。。遮怜。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中顾翼。。奈泪。(33)]
3. mPackageInfo 是什么時候賦值的呢第岖?我們從 ContextImpl 實例化的地方入手,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼期升,跟進代碼發(fā)現(xiàn)峻汉,果不其然,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中冯遂。蕊肥。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)蛤肌,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時壁却,就到了注釋b,而此時mPackageInfo是不為空的裸准,所以會執(zhí)行mPackageInfo.getApplication()展东,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)炒俱,


[圖片上傳中盐肃。爪膊。。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時砸王, mApplication 還沒有被賦值推盛,所以返回的是空,只有把 attachBaseContext方法 執(zhí)行完成后谦铃,mApplication 才會被賦值耘成。
附圖一張:
[圖片上傳中。驹闰。凿跳。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果疮方,并且深入源碼去分析遇到的問題控嗜。文章篇幅不短,希望能對大家有所幫助骡显。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時疆栏,我們想在應用最早啟動時,先做一些判斷惫谤,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務壁顶。
對其他應用提供服務指的是,我們的應用中有 ContentProvider溜歪,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider若专,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果。當?shù)谌綉谜{(diào)用我們的應用時蝴猪,我們的應用存在啟動和未啟動的兩種情況调衰。
剛開始,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中自阱,但等到測試時發(fā)現(xiàn)了很多意想不到的情況嚎莉,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”,但有時候第三方應用能夠使用服務沛豌,而有時候第三方應用不能使等等的問題趋箩。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext加派、onCreate叫确、call 等)啟動順序,跟我們之前理解的稍稍不一樣芍锦。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后竹勉,我們才把遇到的問題徹底解決。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext醉旦、onCreate饶米、call 等)被系統(tǒng)調(diào)用的順序桨啃,我們創(chuàng)建一個簡單的應用车胡,只包含這5個組件檬输,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中匈棘。丧慈。。(3)]
MainService.java
[圖片上傳中主卫。逃默。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時簇搅,均已冷啟動的方式啟動應用完域。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk瘩将。
注意在測試的手機上吟税,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一,點擊桌面的圖標啟動應用姿现,日志如下:

場景二肠仪,通過另外一個應用以啟動Service的形式啟動應用,其中啟動 MainService 的代碼如下:
[圖片上傳中备典。异旧。。(8)]
日志如下:
[圖片上傳中提佣。吮蛹。。(9)]
場景三拌屏,應用通過接受開機廣播啟動的方式啟動匹涮,日志如下:
[圖片上傳中。槐壳。然低。(10)]
場景四,其他應用調(diào)用 ContentProvider 的 call 方法啟動务唐,其中雳攘,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中。枫笛。吨灭。(11)]
日志如下:
[圖片上傳中。刑巧。喧兄。(12)]
結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的无畔;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity吠冤、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法及刻,是在 MainApplication 的 onCreate 方法之后執(zhí)行的蛔钙;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity植影、Service 等的 onCreate(Activity 和 Service 不分先后)企垦;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題涯保,MainApplication 的代碼不變诉濒,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。夕春。未荒。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中及志。片排。。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后困肩,才會執(zhí)行 Application 的 onCreate 的划纽。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎?
為了驗證這個問題锌畸,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中勇劣。。潭枣。(15)]
[圖片上傳中比默。。盆犁。(16)]
我們還在第四個場景下驗證命咐,日志如下:
[圖片上傳中。谐岁。醋奠。(17)]
從日志中可以發(fā)現(xiàn),Application 的 onCreate 執(zhí)行時伊佃,ContentProvider 的 call方法 也在同時執(zhí)行窜司。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行,而是會同時執(zhí)行**航揉。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎塞祈?
有,比如:Application所在類的構造方法帅涂。為了驗證這個問題议薪,將代碼改為:
[圖片上傳中尤蛮。。斯议。(18)]
程序啟動后产捞,日志為:
[圖片上傳中。捅位。轧葛。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用搂抒。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢艇搀?有,自己可以再想想哦求晶。

遇到的坑

好了焰雕,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊芳杏、功能或者參數(shù)矩屁,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中。嗯爵赵,一切感覺起來那么美好吝秕,直到你運行程序崩潰時...
好吧好吧,那些“坑”終于還是來了空幻。
“坑”一:在 Application 的 attachBaseContext方法 中烁峭,使用了 getApplicationContext方法。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時秕铛,內(nèi)心是崩潰约郁。
所以,如果在 attachBaseContext方法 中要使用 context 的話但两,那么使用 **this **吧鬓梅,別再使用 getApplicationContext() 方法了。下文有分析為什么谨湘。
“坑”二:這個其實不算很坑绽快,也不會引起崩潰,但需要注意:
在 Application 的 attachBaseContext方法 中紧阔,去調(diào)用自身的 ContentProvider坊罢,那么這個 ContentProvider 會被初始化兩次,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate寓辱。如果你在 ContentProvider 的 onCreate 中有一些邏輯艘绍,那么一定要檢查是否會有影響。
做一下驗證秫筏,在 Application 中調(diào)用 Provider 的 call方法诱鞠,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法挎挖,Application 的代碼,Provider 的代碼航夺,Activity 的代碼分別如下:
[圖片上傳中蕉朵。。阳掐。(20)]
啟動應用后始衅,日志如下:
[圖片上傳中。缭保。汛闸。(21)]
可以看到,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣)艺骂,而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類诸老,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個。

源碼分析

好了钳恕,現(xiàn)象别伏、問題和“坑”都經(jīng)歷了一遍,那么 為什么 會是這樣呢忧额?
我們通過看源碼厘肮,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序睦番。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null类茂。
先看第一個問題髓需,我們知道Java進程的入口方法一般都是在main中廷区,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建罩息,已經(jīng)幫我們進行封裝崭篡,其實應用 main方法 是在 ActivityThread.java 中的痛黎。
我們查看 ActivityThread.java 的源碼杉辙,本文以下的源碼都以
6.0.1_r10
基礎特愿。
a. ActivityThread.java 的 main方法:
[圖片上傳中配并。丑搔。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中啤月。煮仇。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中谎仲。浙垫。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中。夹姥。杉武。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中。辙售。轻抱。(26)]
f. Application.java 的 attach方法
[圖片上傳中。旦部。祈搜。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中。士八。容燕。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法,而這個方法是會調(diào)用到 installProvider方法 中的曹铃,還是在 ActivityThread.java 中:
[圖片上傳中缰趋。捧杉。陕见。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中。味抖。评甜。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。仔涩。忍坷。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中熔脂,調(diào)用 getApplicationContext() 為 null 呢佩研?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。霞揉。旬薯。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。适秩。绊序。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手秽荞,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼骤公,跟進代碼發(fā)現(xiàn),果不其然扬跋,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中阶捆。。。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)洒试,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時刊咳,就到了注釋b,而此時mPackageInfo是不為空的儡司,所以會執(zhí)行mPackageInfo.getApplication()娱挨,那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說,mPackageInfo是LoadedApk的實例)捕犬,
[圖片上傳中跷坝。。碉碉。(35)]
[圖片上傳中柴钻。。垢粮。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時贴届, mApplication 還沒有被賦值,所以返回的是空蜡吧,只有把 attachBaseContext方法 執(zhí)行完成后毫蚓,mApplication 才會被賦值。
附圖一張:

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html本篇來自 **WizardDragon **的投稿昔善,分享了他對于四大組件啟動時一些方法的調(diào)用順序的研究結果元潘,并且深入源碼去分析遇到的問題。文章篇幅不短君仆,希望能對大家有所幫助翩概。
WizardDragon 的博客地址:
http://blog.csdn.net/long117long

背景

在做一個項目時,我們想在應用最早啟動時返咱,先做一些判斷钥庇,然后根據(jù)判斷的結果再決定要不要對其他應用提供服務。
對其他應用提供服務指的是咖摹,我們的應用中有 ContentProvider评姨,第三方應用通過 call 方法調(diào)用到我們提供的 ContentProvider,ContentProvider 執(zhí)行邏輯后并給調(diào)用的返回結果楞艾。當?shù)谌綉谜{(diào)用我們的應用時参咙,我們的應用存在啟動和未啟動的兩種情況。
剛開始硫眯,我們將判斷邏輯寫在了自定義的 Application 的 onCreate 方法中蕴侧,但等到測試時發(fā)現(xiàn)了很多意想不到的情況,比如:
邏輯判斷之后的結果是不給第三方應用提供“服務”两入,但有時候第三方應用能夠使用服務净宵,而有時候第三方應用不能使等等的問題。
于是我們跟蹤代碼,發(fā)現(xiàn)了 四大組件 以及 Application 的各個方法( attachBaseContext择葡、onCreate紧武、call 等)啟動順序,跟我們之前理解的稍稍不一樣敏储。
在弄清楚了 四大組件 和 Application 在應用冷啟動時的執(zhí)行順序后纲菌,我們才把遇到的問題徹底解決彻舰。

驗證試驗

為了測試 四大組件 和 Application 的各種方法( attachBaseContext队橙、onCreate鸿摇、call 等)被系統(tǒng)調(diào)用的順序,我們創(chuàng)建一個簡單的應用更舞,只包含這5個組件畦幢,不考慮一個應用多進程的情況,代碼分別為:


MainApplication.java

MainActivity.java
[圖片上傳中缆蝉。宇葱。。(3)]
MainService.java
[圖片上傳中刊头。黍瞧。。(4)]
MainReceiver.java

MainProvider.java

在以下幾個場景測試時芽偏,均已冷啟動的方式啟動應用雷逆。
冷啟動,指的是在系統(tǒng)沒有創(chuàng)建apk這個進程時啟動apk污尉。
注意在測試的手機上,不要讓測試的應用被禁止關聯(lián)啟動或自啟動:
場景一往产,點擊桌面的圖標啟動應用被碗,日志如下:

場景二,通過另外一個應用以啟動Service的形式啟動應用仿村,其中啟動 MainService 的代碼如下:
[圖片上傳中锐朴。。蔼囊。(8)]
日志如下:
[圖片上傳中。。。(9)]
場景三玲献,應用通過接受開機廣播啟動的方式啟動,日志如下:
[圖片上傳中富纸。。雏节。(10)]
場景四胜嗓,其他應用調(diào)用 ContentProvider 的 call 方法啟動,其中钩乍,調(diào)用 MainProvider 的 call 代碼如下:
[圖片上傳中辞州。。寥粹。(11)]
日志如下:

結論:
從上面四個場景可以看出:
**1. **Application 的 attachBaseContext 方法是優(yōu)先執(zhí)行的变过;
**2. **ContentProvider 的 onCreate 的方法比 Application 的 onCreate 的方法先執(zhí)行;
**3. **Activity涝涤、Service的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法媚狰,是在 MainApplication 的 onCreate 方法之后執(zhí)行的;
4. 調(diào)用流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity阔拳、Service 等的 onCreate(Activity 和 Service 不分先后)崭孤;

問題

問題一:****ContentProvider 的 onCreate 一定是優(yōu)先于 Application 的 onCreate 執(zhí)行的嗎?
為了驗證這個問題糊肠,MainApplication 的代碼不變辨宠,我們將 MainProvider 的 onCreate 的代碼改為:
[圖片上傳中。货裹。嗤形。(13)]
我們再在上面第四種場景上進行驗證,日志如下:
[圖片上傳中弧圆。赋兵。笔咽。(14)]
問題一結論:
確實是在 ContentProvider 的 onCreate 執(zhí)行完成之后,才會執(zhí)行 Application 的 onCreate 的霹期。
問題二:****ContentProvider中 的 call方法 是在 Application 的 onCreate 執(zhí)行完之后才執(zhí)行的嗎叶组?
為了驗證這個問題,我們將 MainProvider 和 MainApplication 的代碼改為:
[圖片上傳中经伙。扶叉。。(15)]
[圖片上傳中帕膜。枣氧。。(16)]
我們還在第四個場景下驗證垮刹,日志如下:
[圖片上傳中达吞。。荒典。(17)]
從日志中可以發(fā)現(xiàn)酪劫,Application 的 onCreate 執(zhí)行時,ContentProvider 的 call方法 也在同時執(zhí)行寺董。
問題二結論:
Application 的 onCreate方法 和 Provider 的 call方法** 不是順序執(zhí)行覆糟,而是會同時執(zhí)行**。
問題三:****有比 Application 的 attachBaseContext方法 更早執(zhí)行的方法嗎遮咖?
有滩字,比如:Application所在類的構造方法。為了驗證這個問題御吞,將代碼改為:
[圖片上傳中麦箍。。陶珠。(18)]
程序啟動后挟裂,日志為:
[圖片上傳中。揍诽。诀蓉。(19)]
問題三結論:
Application 的構造方法早于 Application 的 attachBaseContext方法 調(diào)用。
那么有沒有比 Application 的構造方法還早被調(diào)用的方法呢暑脆?有交排,自己可以再想想哦。

遇到的坑

好了饵筑,我們知道 attachBaseContext 的方法在“一般情況下是最早執(zhí)行的“,那么在項目中為了能”盡早“的提前初始化某些模塊处坪、功能或者參數(shù)根资,那么我們會把代碼從 Application 的onCreate方法 提前到 attachBaseContext方法 中架专。嗯,一切感覺起來那么美好玄帕,直到你運行程序崩潰時...
好吧好吧部脚,那些“坑”終于還是來了。
“坑”一:在 Application 的 attachBaseContext方法 中裤纹,使用了 getApplicationContext方法委刘。
當我發(fā)現(xiàn)在 attachBaseContext方法 中使用 getApplicationContext方法 返回null時汗捡,內(nèi)心是崩潰爵卒。
所以堪唐,如果在 attachBaseContext方法 中要使用 context 的話鹃骂,那么使用 **this **吧宅荤,別再使用 getApplicationContext() 方法了愚铡。下文有分析為什么突勇。
“坑”二:這個其實不算很坑健芭,也不會引起崩潰奸汇,但需要注意:
在 Application 的 attachBaseContext方法 中施符,去調(diào)用自身的 ContentProvider,那么這個 ContentProvider 會被初始化兩次擂找,也就是說這個 ContentProvider 會被兩次調(diào)用到onCreate戳吝。如果你在 ContentProvider 的 onCreate 中有一些邏輯,那么一定要檢查是否會有影響贯涎。
做一下驗證听哭,在 Application 中調(diào)用 Provider 的 call方法,并在 MainActivity 中的 onCreate方法 中調(diào)用 Provider 的 call方法柬采,Application 的代碼欢唾,Provider 的代碼,Activity 的代碼分別如下:
[圖片上傳中粉捻。礁遣。。(20)]
啟動應用后肩刃,日志如下:
[圖片上傳中祟霍。。盈包。(21)]
可以看到沸呐,MainProvider 的 onCreate 的方法被調(diào)用了兩次(因為 MainProvider 的兩次 onCreate 打印出的自身對象不一樣),而在 MainActivity 中調(diào)用到 call方法 執(zhí)行的類呢燥,跟 MainApplication 在 attachBaseContext方法 執(zhí)行的類是同一個崭添。

源碼分析

好了,現(xiàn)象叛氨、問題和“坑”都經(jīng)歷了一遍呼渣,那么 為什么 會是這樣呢棘伴?
我們通過看源碼屁置,來跟蹤:
1. Application 的 attachBaseContext、ContentProvider 的 onCreate 以及 Application 的 onCreate 在源碼中的調(diào)用順序蓝角。
2. 為什么在 Application 的 attachBaseContext 中調(diào)用 getApplicationContext 得到的是null阱穗。
先看第一個問題,我們知道Java進程的入口方法一般都是在main中使鹅,而Android為了讓應用開發(fā)者不需要關心應用的創(chuàng)建揪阶,已經(jīng)幫我們進行封裝,其實應用 main方法 是在 ActivityThread.java 中的并徘。
我們查看 ActivityThread.java 的源碼遣钳,本文以下的源碼都以
6.0.1_r10
基礎。
a. ActivityThread.java 的 main方法:
[圖片上傳中麦乞。蕴茴。。(22)]
b. ActivityThread.java 的 attach方法:
[圖片上傳中姐直。倦淀。。(23)]
c. ActivityThread.java 的 handleBindApplication(AppBindData data)方法:
[圖片上傳中声畏。撞叽。。(24)]
d. LoaderApk.java 的 makeApplication方法
[圖片上傳中插龄。愿棋。。(25)]
e. Instrumentation.java的相關方法
[圖片上傳中均牢。糠雨。。(26)]
f. Application.java 的 attach方法
[圖片上傳中徘跪。甘邀。。(27)]
g. ActivityThread.java 的 handleBindApplication方法:
[圖片上傳中垮庐。松邪。。(28)]
h. 繼續(xù)跟蹤 installContentProviders 這個方法哨查,而這個方法是會調(diào)用到 installProvider方法 中的逗抑,還是在 ActivityThread.java 中:
[圖片上傳中。。锋八。(29)]
i. 看 ContentProvider.java 中的 attachInfo方法(frameworks/base/core/java/android/content/ContentProvider.java)
[圖片上傳中浙于。。挟纱。(30)]
j. 關于 注釋7 的 mInstrumentation.callApplicationOnCreate(app) 調(diào)用到的 Instrumentation.java 中的方法
[圖片上傳中。腐宋。紊服。(31)]
看第二問題,為什么在我們自定義 Application 中的 attachBaseContext方法 中胸竞,調(diào)用 getApplicationContext() 為 null 呢欺嗤?
1. 跟蹤 getApplicationContext() 發(fā)現(xiàn)是在 ContextWrapper.java 中實現(xiàn)的:
[圖片上傳中。卫枝。煎饼。(32)]
2. 我們看 ContextImpl 的 getApplicationContext方法:
[圖片上傳中。校赤。吆玖。(33)]
3. mPackageInfo 是什么時候賦值的呢?我們從 ContextImpl 實例化的地方入手马篮,在注釋 5.1 之前的一行代碼看到了 ContextImpl 的實例化代碼沾乘,跟進代碼發(fā)現(xiàn),果不其然浑测,看到了 mPackageInfo 被賦值的地方:
[圖片上傳中翅阵。。迁央。(34)]
4. 注釋b.1所在的流程 早于 注釋5.4 的(在注釋5.4時才調(diào)用到了Application的attachBaseContext方法)掷匠,在我們自定義的Application中attachBaseContext調(diào)用getApplicationContext方法時,就到了注釋b岖圈,而此時mPackageInfo是不為空的讹语,所以會執(zhí)行mPackageInfo.getApplication(),那么我們再看一下LoadedApk.java中的getApplication方法(正如前面所說幅狮,mPackageInfo是LoadedApk的實例)募强,
[圖片上傳中荆陆。少梁。。(35)]
[圖片上傳中鲫忍。逐抑。鸠儿。(36)]
看到這里找到原因所在了:
因為我們在 Application 的 attachBaseContext方法 中調(diào)用 getApplicationContext() 時, mApplication 還沒有被賦值,所以返回的是空进每,只有把 attachBaseContext方法 執(zhí)行完成后汹粤,mApplication 才會被賦值。
附圖一張:
[圖片上傳中田晚。嘱兼。。(37)]

參考

http://androidxref.com

http://blog.csdn.net/u011228356/article/details/45102623

http://www.wtoutiao.com/p/1f8OfGz.html請支持原文作者 : 鴻洋
本文由馬北劍西投稿贤徒。
馬北劍西的博客地址:
http://blog.csdn.net/mabeijianxi/

之前還推送給過一篇:仿微信視頻拍攝UI, 基于ffmpeg的視頻錄制編輯芹壕,可以一起學習~

1

概述

本庫暫時是在秒拍開源庫上做的二次開發(fā),旨在開發(fā)簡單好用高效的視頻錄制庫接奈。本篇文檔只涉及Java層次邏輯踢涌,正在業(yè)余修煉c語言與JNI相關的東西,如果有幸寫第二篇文章序宦,那時將對其做更深入的剖析睁壁,如FFmpeg編譯、JNI相關代碼編寫互捌。

效果圖:

[圖片上傳中潘明。。疫剃。(1)]

功能描述:

利用FFmpeg錄制定制化的視頻钉疫,并可對其定制化的壓縮處理。如設置視頻尺寸巢价、設置碼率牲阁、碼率模式、幀率壤躲、視頻質(zhì)量等級城菊、壓縮速度等等,當然這些只是暫時的碉克,后期會繼續(xù)維護凌唬。

項目地址:
https://github.com/mabeijianxi/small-video-record.

2

使用方法

1:添加依賴
compile 'com.mabeijianxi:small-video-record:1.2.0'

2:在manifests里面添加

<activity android:name="mabeijianxi.camera.MediaRecorderActivity"/>

3:在Application里面初始化小視頻錄制:

[圖片上傳中。漏麦。客税。(2)]

4:跳轉(zhuǎn)錄制界面:

[圖片上傳中。撕贞。更耻。(3)]

3

原理講解

基本過程就是調(diào)用系統(tǒng)camera與AudioRecord得到視頻和音頻的byte回調(diào),然后出入配置好參數(shù)FFmpeg捏膨,結束后得到目標視頻秧均。

(1)配置Camera參數(shù):

首先我們錄制的視頻是豎著的食侮,所以需要旋轉(zhuǎn)90°(默認是橫屏錄制):camera.setDisplayOrientation(90);

然后設置顯示控件:camera.setPreviewDisplay(mSurfaceHolder)目胡;

幀率設置:這個參數(shù)是可傳入的锯七,但是每個攝像頭所支持的大小是不一樣的,所以你傳入maxFrameRate我會再校驗一遍誉己,如果當前攝像頭支持此幀率那么就使用眉尸,如果不支持那么就選擇個最接近且小于它的,如果你值很小有可能還是找不到巫延,這時就選擇最小的一個效五,具體算法如下:

[圖片上傳中。炉峰。。(4)]

攝像頭輸出尺寸設置:

通過系統(tǒng)API mParameters.getSupportedPreviewSizes()可以得到當前攝像頭所支持的尺寸脉执,注意這里返回的Size里面其height對應的屏幕短邊疼阔,width對應的是屏幕長邊,也就是說我們也要校驗傳入的smallVideoWidth是否支持半夷,當然smallVideoHeight不需要校驗婆廊,

因為是小視頻,我們到時候說不定還會剪切掉一部分巫橄,校驗完成即可得到傳入的smallVideoWidth所對應的且攝像頭所支持的對應高度淘邻,把這個寬高設置上即可。常見的smallVideoWidth 有480湘换、720宾舅、1080等等。

具體如下:

[圖片上傳中彩倚。筹我。。(5)]

設置采樣率:

常用格式有兩種:NV21 / YV12,mParameters.setPreviewFormat(ImageFormat.NV21)

(2)接收設備并傳入FFmpeg音(音頻具體可參考AudioRecorder類)視頻數(shù)據(jù):

這里首先需要知道幾個FFmpeg命令:
-vf 可以添加濾鏡帆离,特別強大蔬蕊,可以旋轉(zhuǎn)縮放剪切等等,我們需要用到旋轉(zhuǎn)和剪切(我一直考慮需不需要用縮放的方式哥谷,因為這樣可以在預覽界面設置高分辨率看著清晰一些)岸夯。

transpose,旋轉(zhuǎn),對應的值有0们妥、1猜扮、2、3王悍,0:逆時針旋轉(zhuǎn)90°然后垂直翻轉(zhuǎn)1:順時針旋轉(zhuǎn)90°破镰,2:逆時針旋轉(zhuǎn)90°,3:順時針旋轉(zhuǎn)90°然后水平翻轉(zhuǎn)。

剪切鲜漩,關鍵字是crop,其有四個參數(shù)源譬,分別是寬度、高度孕似、其實剪切位置的X值與Y值踩娘,如ffmpeg -i a.mp4 -vf crop=480:360:0:0...;

-vcodec 指定視頻編解碼器;

-acodec 指定音頻編解碼器喉祭;

vbr 動態(tài)碼率养渴;

cbr 靜態(tài)碼率;

-crf 視頻質(zhì)量等級051泛烙,越大質(zhì)量越差理卑,建議1828即可,與cbr模式不兼容蔽氨;

-preset 轉(zhuǎn)碼速度藐唠,快慢的優(yōu)劣應該都懂的,可根據(jù)自己業(yè)務場景設置鹉究,具體有:ultrafast掏父、superfast响疚、veryfast窘茁、faster背犯、fast、medium绍妨、slow润脸、slower、veryslow痘绎、placebo津函;

-i 指定輸入;

-x264opts 配置其編解碼參數(shù)孤页;

maxrate 最大碼率尔苦;

bitrate 固定碼率;

-f 輸出格式;

-s 設置幀大小。格式為 ‘wxh’;

-ss 指定開始時間;

-vframes 指定多少幀;

接著皆可在錄制前配置我們的錄制參數(shù):

[圖片上傳中行施。允坚。。(6)]

我們這里設置了旋轉(zhuǎn)濾鏡與剪切濾鏡蛾号,由于我們錄制豎屏視頻所以旋轉(zhuǎn)90°稠项,然后剪切為我們制定的視頻尺寸。當然里面還有三個get函數(shù)鲜结,分別是視頻質(zhì)量等級展运、轉(zhuǎn)碼速度活逆、碼率模式

視頻質(zhì)量等級命令為-crf [size]:

[圖片上傳中拗胜。蔗候。。(7)]

轉(zhuǎn)碼速度命令為-preset [what]:

[圖片上傳中埂软。锈遥。。(8)]

碼率模式: 碼率模式分為vbr與cbr,我在里面加了三個類AutoVBRMode勘畔、VBRMode所灸、CBRMode,三者都可傳入轉(zhuǎn)碼速度。如果不想管那么多那么只需傳入無參的AutoVBRMode對象即可炫七,只有AutoVBRMode模式下可以傳入視頻質(zhì)量等級值爬立,這個值將最大程度上控制視頻質(zhì)量。VBRMode模式下可以指定最大碼率與額定碼率万哪。懦尝、CBRMode模式下出入一個固定碼率即可。

[圖片上傳中壤圃。。琅轧。(9)]

配置好后即可開始錄制伍绳,在camera的數(shù)據(jù)回調(diào)里面把數(shù)據(jù)轉(zhuǎn)入底層。

[圖片上傳中乍桂。冲杀。。(10)]

(3)多段視頻合并

錄制過程中我們可以暫停錄制睹酌,這個可能生成n段短視頻权谁,這個我們就需要合并視頻了,利用FFmpeg命令也可以輕松實現(xiàn):

[圖片上傳中憋沿。旺芽。。(11)]

這里視頻和音頻的編解碼器使用原始數(shù)據(jù)的即可辐啄,命令為-vcodec copy -acodec copy這樣速度回比較快,-absf表示為匹配的流設置比特流過濾器,當然還有-vbsf采章,最新的指定方式是-bsf:v

(4)進一步轉(zhuǎn)碼壓縮

如果沒有設置 doH264Compress 參數(shù)那么將不執(zhí)行以下邏輯

[圖片上傳中。壶辜。悯舟。(12)]

上面我們指定了視頻編解碼器為libx264,音頻編解碼器為libfdkaac,然后跟你個性化沖入的doH264Compress 參數(shù)進行壓縮砸民,結束后我們就得到了壓縮好的視頻了抵怎。

(5)截取視頻中的一幀作為封面

[圖片上傳中奋救。。反惕。(13)]

總結

本庫的優(yōu)點是簡單便捷尝艘,可控性強,后期將繼續(xù)維護承璃。

缺點是FFmpeg優(yōu)點老利耍,后期會考慮自己編譯一份,那時利用FFmpeg玩轉(zhuǎn)Android視頻錄制與壓縮(二)也就出來了盔粹,優(yōu)點開始期待了隘梨,有興趣的同學

注意:

編譯環(huán)境請滿足:targetSdkVersion<=22

出現(xiàn) java.lang.UnsatisfiedLinkError錯誤可以嘗試在gradle.properties中添加:android.useDeprecatedNdk=true,然后在主module的build.gradle中配置ndk {abiFilters "armeabi", "armeabi-v7a"}

歡迎到我github上指教
https://github.com/mabeijianxi/small-video-record

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舷嗡,一起剝皮案震驚了整個濱河市轴猎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌进萄,老刑警劉巖捻脖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異中鼠,居然都是意外死亡可婶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門援雇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矛渴,“玉大人,你說我怎么就攤上這事惫搏【呶拢” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵筐赔,是天一觀的道長铣猩。 經(jīng)常有香客問我,道長茴丰,這世上最難降的妖魔是什么达皿? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮较沪,結果婚禮上鳞绕,老公的妹妹穿的比我還像新娘。我一直安慰自己尸曼,他們只是感情好们何,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著控轿,像睡著了一般冤竹。 火紅的嫁衣襯著肌膚如雪拂封。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天鹦蠕,我揣著相機與錄音彪腔,去河邊找鬼捌治。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的玄柏。 我是一名探鬼主播风喇,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼饥臂,長吁一口氣:“原來是場噩夢啊……” “哼街佑!你這毒婦竟也來了?” 一聲冷哼從身側響起屹徘,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤走趋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后噪伊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體簿煌,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年鉴吹,在試婚紗的時候發(fā)現(xiàn)自己被綠了姨伟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡豆励,死狀恐怖授滓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肆糕,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布在孝,位于F島的核電站诚啃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏私沮。R本人自食惡果不足惜始赎,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仔燕。 院中可真熱鬧造垛,春花似錦、人聲如沸晰搀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽外恕。三九已至杆逗,卻和暖如春乡翅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罪郊。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工蠕蚜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悔橄。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓靶累,卻偏偏與公主長得像,于是被迫代替她去往敵國和親癣疟。 傳聞我的和親對象是個殘疾皇子挣柬,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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