到底getApplicationContext和getApplication是不是返回同一個物件?

SillyMonkey發表於2019-04-04

前言

在上篇文章從getApplicationContext和getApplication再次梳理Android的Application正確用法中,我提到

但是我們知道了mApplication和context是兩個不同的東西,所以嚴格意義上來說getApplicationContext和getApplication是不一樣的,雖然很多時候他們返回的都是同一個物件

注意到我這裡說的是這兩個方法返回的物件是不一樣的,因為我看到Activity中這兩個方法返回了兩個物件,就單純的以為他們真的是不一樣的,看來真是淺嘗輒止了,做了個錯誤示範,程式碼還是要刨根問底啊。

找不同

今天來做一個糾正和補充,我們來繼續往下看程式碼,看看他們是不是真的不一樣,還是有相似之處:

public abstract Context getApplicationContext();
複製程式碼

getApplicationContext我們知道是一個抽象方法,他的真正實現是在ContextImpl中:

@Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
複製程式碼

再來看看getApplication方法(只存在於Activity和Service中):

public final Application getApplication() {
        return mApplication;
    }
複製程式碼

那mApplication的賦值在哪?搜尋一下,只有一個地方有賦值:

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) {
        attachBaseContext(context);
        .......
        mApplication = application;
        }

複製程式碼

在上篇文章中,我看到了這裡就覺得這兩個方法返回的物件不一樣,可是我們忽略了getApplicationContext這個方法,當mPackageInfo不為空和為空是分別呼叫了mPackageInfo.getApplication()和mMainThread.getApplication(),那getApplicationContext到底返回的東西跟mApplication有什麼不同,來看看這兩個方法,在LoadedApk.java中看到mPackageInfo.getApplication()

Application getApplication() {
        return mApplication;
    }
複製程式碼

LoadedApk也有一個mApplication,這個mApplication的賦值在LoadedApk的makeApplication:

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
...
if (mApplication != null) {
        return mApplication;
}
Application app = null;
...
app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
...
mActivityThread.mAllApplications.add(app);
        mApplication = app;
...
}

複製程式碼

看到首先是一個空判斷(單例),為空的話新建了一個Application然後賦值給mApplication,我們再看看mMainThread.getApplication()返回了什麼,在ActivityThread.java中:

public Application getApplication() {
        return mInitialApplication;
    }
複製程式碼

再來看看mInitialApplication的賦值在哪裡:

private void handleBindApplication(AppBindData data) {
...
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
...

}
複製程式碼

我們又看到了makeApplication,至於data.info也是LoadedApk這個類,看到這裡我們就一目瞭然了,繞來繞去結果都是同一個東西,只是可能建立的時機不同,一個是在LoadedApk,一個是在ActivityThread,不過最後我們發現這個getApplicationContext()返回的都是mApplication

真相大白

這個命名就很有意思了,在LoadedApk我們看到了一個叫mApplication的東西,在Activity也有一個叫mApplication,那他們是不是有什麼聯絡呢?來看看在ActivitymApplication的賦值,在attach方法中找到了它(方法中的其他引數我去掉了):

final void attach(Application application) {
mApplication = application;
}
複製程式碼

也就是說等於呼叫attach方法時傳入的application,那Activity的attach是在哪裡呼叫呢,我們要來到反覆提到的一個應用程式入口類ActivityThread,它有一個performLaunchActivity的方法,用來載入一個Activity,這裡就有attach()的呼叫(我去掉了其他引數):

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
activity.attach(app);
...
}
複製程式碼

我們發現又來了。。。熟悉的makeApplication(),r.packageInfo果然是LoadedApk類,最後殊途同歸,又來到了這個單例,返回程式唯一的mApplication,還是一樣的配方。。。

結果

結果很明顯了,標題的問題已解,getApplicationContext和getApplication返回的是不是同一個物件?答:是的!

當然話不能說的那麼死,他們相同的前提是mApplication不為空,話又說回來,這個是全域性的上下文,程式都啟動了他怎麼會為空呢,至於它到底什麼情況會為空造成返回的物件不一樣呢,待武功精進了繼續分解。。。

相關文章