本文打算從原理出發(fā)秩伞,通過(guò)分析源碼逞带,找到Activity的成員變量Application的出處,以此分析倆個(gè)方法的區(qū)別纱新。
背景知識(shí)
首先上一張老生常談的圖展氓。
由圖的繼承關(guān)系可知,Activity脸爱、Application遇汞、Service都繼承自ContextWrapper;
ContextWrapper是個(gè)代理類簿废,它的部分源碼截圖如下:
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
...
}
從源碼可以看出空入,ContextWrapper的方法都是通過(guò)代理"另一個(gè)"Context實(shí)現(xiàn)的。這里暫時(shí)給出結(jié)論:這個(gè)代理類就是ContextImpl族檬,它是Context的真正實(shí)現(xiàn)類歪赢。稍后我們會(huì)用源碼驗(yàn)證結(jié)論。
也就是說(shuō)导梆,Activity內(nèi)部有成員變量ContextImpl的實(shí)例轨淌,以此實(shí)現(xiàn)我們常用的Context的方法迂烁。
源碼分析
了解了上述背景知識(shí)之后,下面來(lái)正式開(kāi)始分析Activity的方法:getApplication()
和getApplicationContext()
的區(qū)別递鹉。
getApplication
我們先來(lái)看getApplication()
的源碼:
public final Application getApplication() {
return mApplication;
}
Activity內(nèi)部有成員變量mApplication盟步,getApplication()
直接返回的就是它,那么它的來(lái)源是哪呢躏结?
這里為了使流程更加清晰却盘,我將從源碼源頭分析,正推出結(jié)果媳拴,實(shí)際學(xué)習(xí)則是通過(guò)結(jié)果逆向反推出源頭的黄橘。
ActivityThread是Activity的啟動(dòng)類,通過(guò)調(diào)用ActivityThread.performLaunchActivity()
屈溉,即可生成要啟動(dòng)的Activity實(shí)例塞关,本文不會(huì)專程去分析應(yīng)用程序或Activity的啟動(dòng)原理,只需要知道它是Activity實(shí)例的創(chuàng)建方法就可以了子巾。下面我們來(lái)看下這個(gè)方法的部分源碼:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation); #1
...
if (activity != null) {
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);#2
...
注釋1處帆赢,調(diào)用r.packageInfo.makeApplication
生成了Application實(shí)例;注釋2處线梗,調(diào)用activity.attch
椰于,將Application傳給了Activity。來(lái)看下``
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
...
mApplication = application; #1
注釋1處仪搔,直接將application賦值給了mApplication瘾婿,看來(lái)這就是我們要找的源頭。
回到上一步烤咧,r.packageInfo.makeApplication
生成了Application實(shí)例偏陪,這里,r.packageInfo
實(shí)際是LoadedApk對(duì)象髓削,故名思義竹挡,它通過(guò)ClassLoader去加載APK文件,以此來(lái)獲取我們需要的類立膛。不明白ClassLoader可以稍微去了解下,不了解也沒(méi)關(guān)系梯码,只需要知道通過(guò)ClassLoader可以將類文件轉(zhuǎn)化為類就可以了宝泵。
下面來(lái)看LoadedApk.makeApplication()
源碼:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
...
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}#1
try {
java.lang.ClassLoader cl = getClassLoader(); #2
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); #3
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);#4
...
} catch (Exception e) {
...
}
...
mApplication = app;
...
return app;
}
這里為了理清流程,省略了部分無(wú)關(guān)主題的代碼轩娶。
注釋1處儿奶,獲取要?jiǎng)?chuàng)建的Application的ClassName,如果沒(méi)有自定義的Application鳄抒,則使用系統(tǒng)原生"android.app.Application"
闯捎;
注釋2處椰弊,獲取ClassLoader,它可以通過(guò)ClassName獲取需要的Class瓤鼻。
注釋3處秉版,獲取ContextImpl,上文說(shuō)過(guò)Application繼承自ContextWrapper茬祷,它僅僅是Context的代理清焕,真正的實(shí)現(xiàn)類是ContextImpl。這一結(jié)論很快將得到證實(shí)祭犯。
在得到必要的三個(gè)參數(shù)之后秸妥,就可以創(chuàng)建Application了,注釋4處執(zhí)行了創(chuàng)建Application的方法:
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
通過(guò)ClassLoader和ClassName沃粗,獲取到Class<Application>
實(shí)例粥惧,然后執(zhí)行下一步:
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance(); #1
app.attach(context); #2
return app;
}
注釋1處,通過(guò)class反射創(chuàng)建了Application實(shí)例最盅。這就是getApplication()
的最終源頭影晓。
但是還沒(méi)有完,注釋2處調(diào)用了app.attach(context);
檩禾,聯(lián)系上下文可以知道這個(gè)context就是之前創(chuàng)建的ContextImpl
挂签,而attach
最終調(diào)用的是ContextWrapper.attachBaseContext
,還記得文章開(kāi)頭的ContextWrapper源碼嗎盼产?找一下這個(gè)方法饵婆,你就會(huì)發(fā)現(xiàn),Application作為ContextWrapper戏售,其真實(shí)調(diào)用的Context就是這里傳過(guò)來(lái)的ContextImpl侨核!
講到這里,我們已經(jīng)找到了getApplication()
的源頭灌灾。同時(shí)也會(huì)發(fā)現(xiàn):ContextImpl從始至終都沒(méi)有與getApplication()
有任何關(guān)聯(lián)搓译,那為什么還要大費(fèi)周章的講它呢?別急锋喜,還有一個(gè)方法getApplicationContext()
沒(méi)說(shuō)呢些己。
getApplicationContext
先來(lái)看一下Activity.getApplicationContext()
源碼,該方法是Activity的父類:ContextWrapper實(shí)現(xiàn)的嘿般。
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
通過(guò)上文可知段标,mBase即是ContextImpl(但是注意不是上文Application的ContextImpl,下文會(huì)有分析)炉奴,來(lái)看下ContextImpl.getApplicationContext()
的源碼:
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
mPackageInfo就是LoadedApk實(shí)例逼庞,ContextImpl.LoadedApk.getApplication()
直接返回了LoadedApk的成員變量mApplication。從這里還無(wú)從得知mApplication的來(lái)源瞻赶,所以我們可以先分析ContextImpl
實(shí)例的來(lái)源赛糟。
回到上文中Activity實(shí)例的創(chuàng)建方法performLaunchActivity
:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ContextImpl appContext = createBaseContextForActivity(r); #1
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);#2
...
注釋1處派任,創(chuàng)建了ContextImpl實(shí)例。
注釋2處璧南,調(diào)用了activity.attach
掌逛,還記得它的源碼嗎?
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context); #1
...
mApplication = application;
注釋1處穆咐,ContextImpl被賦值給了Activity颤诀,看來(lái)這就是ContextImpl的最初源頭。
回到上一步注釋1處对湃,來(lái)看下ContextImpl實(shí)例的創(chuàng)建過(guò)程createBaseContextForActivity(r)
:
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
...
return appContext;
}
第二個(gè)參數(shù)r.packageInfo
就是LoadedApk對(duì)象崖叫,它在這里賦值給了ContextImpl。聯(lián)系上下文拍柒,也就是說(shuō)心傀,getApplicationContext()
的application,就來(lái)自r.packageInfo
的成員變量mApplication拆讯。
那么脂男,r.packageInfo
的成員變量mApplication,又是什么時(shí)候賦值的呢种呐?
還記得上文Application是怎么創(chuàng)建的嗎宰翅?
r.packageInfo.makeApplication(false, mInstrumentation);
再來(lái)回顧一遍LoadedApk.makeApplication
源碼,這次只需要關(guān)注注釋1處:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
...
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
...
} catch (Exception e) {
...
}
...
mApplication = app; #1
...
return app;
}
在上文中創(chuàng)建的Application爽室,在賦值給Activity的成員變量mApplication的同時(shí)汁讼,還賦值給了LoadedApk的mApplication,之后LoadedApk又作為了ContextImpl的成員變量之一傳給了Activity阔墩。
至此得知嘿架,對(duì)于Activity而言,getApplication()
和getApplicationContext()
返回的是同一個(gè)Application啸箫,它們都是LoadedApk通過(guò)ClassLoader創(chuàng)建的耸彪,只不過(guò)賦值給了不同的成員變量而已。
流程圖
下面梳理下它們的創(chuàng)建與賦值流程忘苛。
getApplication
getApplicationContext
由流程圖可以看出蝉娜,Application的來(lái)源均是LoadedApk.makeApplication()
,故倆個(gè)方法返回的是同一個(gè)Application柑土。
寫在最后
分析倆個(gè)方法的區(qū)別蜀肘,旨在理清Android的啟動(dòng)流程,加深對(duì)Android系統(tǒng)的理解稽屏,重在過(guò)程,而非結(jié)論西乖。