網上關於 Context 的文章也已經有很多了,好比值得參考的有:android
Android Context徹底解析,你所不知道的Context的各類細節數據庫
但看了一下,發現還有值得討論的地方,好比這個等式:app
Context個數 = Service 個數 + Activity 個數 + 1ide
老實說,我不明白這個等式有什麼意義,並且仍是錯的。首先多進程狀況下,Application 對象就不止一個;其次,Activity、Service、Application 繼承自 ContextWrapper,它們本身就是一個 Context,裏面又有一個 Base Context;最後,還有各類 outer context、display context 什麼的,這部分沒深刻研究過,但 Context 的數量絕對大於上述等式的兩倍了。oop
上面這部分算一個討論,下面正式進入正題。ui
Context 自己是一個抽象類,主要實現類爲 ContextImpl,另外有子類 ContextWrapper 和 ContextThemeWrapper,這兩個子類都是 Context 的代理類,主要區別是 ContextThemeWrapper 有本身的主題資源。它們繼承關係以下:this
若是要弄清楚 「某個類有什麼用」 這樣的問題,其實很簡單,看一下它提供了什麼接口就知道了,下面列舉一些主要的:.net
/** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */ public abstract class Context { // 四大組件相關 public abstract void startActivity(@RequiresPermission Intent intent); public abstract void sendBroadcast(@RequiresPermission Intent intent); public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter); public abstract void unregisterReceiver(BroadcastReceiver receiver); public abstract ComponentName startService(Intent service); public abstract boolean stopService(Intent service); public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags); public abstract void unbindService(@NonNull ServiceConnection conn); public abstract ContentResolver getContentResolver(); // 獲取系統/應用資源 public abstract AssetManager getAssets(); public abstract Resources getResources(); public abstract PackageManager getPackageManager(); public abstract Context getApplicationContext(); public abstract ClassLoader getClassLoader(); public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) { ... } public final String getString(@StringRes int resId) { ... } public final int getColor(@ColorRes int id) { ... } public final Drawable getDrawable(@DrawableRes int id) { ... } public abstract Resources.Theme getTheme(); public abstract void setTheme(@StyleRes int resid); public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... } // 獲取應用相關信息 public abstract ApplicationInfo getApplicationInfo(); public abstract String getPackageName(); public abstract Looper getMainLooper(); public abstract int checkPermission(@NonNull String permission, int pid, int uid); // 文件相關 public abstract File getSharedPreferencesPath(String name); public abstract File getDataDir(); public abstract boolean deleteFile(String name); public abstract File getExternalFilesDir(@Nullable String type); public abstract File getCacheDir(); ... public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode); public abstract boolean deleteSharedPreferences(String name); // 數據庫相關 public abstract SQLiteDatabase openOrCreateDatabase(...); public abstract boolean deleteDatabase(String name); public abstract File getDatabasePath(String name); ... // 其它 public void registerComponentCallbacks(ComponentCallbacks callback) { ... } public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... } ... } public interface ComponentCallbacks { void onConfigurationChanged(Configuration newConfig); void onLowMemory(); }
結合註釋,能夠發現,Context 就至關於 Application 的大管家,主要負責:代理
先看 ContextWrapper:
/** * Proxying implementation of Context that simply delegates all of its calls to * another Context. Can be subclassed to modify behavior without changing * the original Context. */ 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; } // 這就是常常讓人產生疑惑的 Base Context 了 public Context getBaseContext() { return mBase; } // 下面這些方法全都直接經過 mBase 完成 @Override public AssetManager getAssets() { return mBase.getAssets(); } @Override public Resources getResources() { return mBase.getResources(); } @Override public PackageManager getPackageManager() { return mBase.getPackageManager(); } ... }
能夠看到,ContextWrapper 實際上就是 Context 的代理類而已,全部的操做都是經過內部成員 mBase 完成的,另外,Activity、Service 的 getBaseContext 返回的就是這個 mBase。
接着看 ContextThemeWrapper,這個類的代碼並很少,主要看 Resource 和 Theme 相關的:
/** * A context wrapper that allows you to modify or replace the theme of the * wrapped context. */ public class ContextThemeWrapper extends ContextWrapper { private int mThemeResource; private Resources.Theme mTheme; private LayoutInflater mInflater; private Configuration mOverrideConfiguration; private Resources mResources; public ContextThemeWrapper() { super(null); } public ContextThemeWrapper(Context base, @StyleRes int themeResId) { super(base); mThemeResource = themeResId; } public ContextThemeWrapper(Context base, Resources.Theme theme) { super(base); mTheme = theme; } @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); } // 在 Recource 初始化以前,傳入配置信息 public void applyOverrideConfiguration(Configuration overrideConfiguration) { if (mResources != null) { throw new IllegalStateException(...); } if (mOverrideConfiguration != null) { throw new IllegalStateException(...); } mOverrideConfiguration = new Configuration(overrideConfiguration); } public Configuration getOverrideConfiguration() { return mOverrideConfiguration; } // 沒有重寫 setResource,即 setResource 行爲和父類同樣 @Override public Resources getResources() { return getResourcesInternal(); } private Resources getResourcesInternal() { if (mResources == null) { if (mOverrideConfiguration == null) { mResources = super.getResources(); } else { // 根據配置信息初始化 Resource // 注意,這裏建立了另外一個和 Base Context 不一樣的 Resource final Context resContext = createConfigurationContext(mOverrideConfiguration); mResources = resContext.getResources(); } } return mResources; } @Override public void setTheme(int resid) { if (mThemeResource != resid) { mThemeResource = resid; initializeTheme(); } } private void initializeTheme() { final boolean first = mTheme == null; if (first) { // 根據 Resource 獲取 Theme mTheme = getResources().newTheme(); // 複製內容 final Resources.Theme theme = getBaseContext().getTheme(); if (theme != null) { mTheme.setTo(theme); } } onApplyThemeResource(mTheme, mThemeResource, first); } protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) { theme.applyStyle(resId, true); } @Override public Resources.Theme getTheme() { // 只會初始化一次 if (mTheme != null) { return mTheme; } mThemeResource = Resources.selectDefaultTheme(mThemeResource, getApplicationInfo().targetSdkVersion); initializeTheme(); return mTheme; } ... }
結合註釋及源碼,能夠發現,相比 ContextWrapper,ContextThemeWrapper 有本身的另外 Resource 以及 Theme 成員,而且能夠傳入配置信息以初始化本身的 Resource 及 Theme。即 Resource 以及 Theme 相關的行爲再也不是直接調用 mBase 的方法了,也就說,ContextThemeWrapper 和它的 mBase 成員在 Resource 以及 Theme 相關的行爲上是不一樣的。
下面看一下 ContextImpl 有關 Theme 以及 Resource 的部分,以分析它和 ContextThemeWrapper 的區別:
/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */ class ContextImpl extends Context { private int mThemeResource = 0; private Resources.Theme mTheme = null; private @NonNull Resources mResources; // 用於建立 Activity Context static ContextImpl createActivityContext(...) { ContextImpl context = new ContextImpl(...); context.setResources(resourcesManager.createBaseActivityResources(...)); return context; } // 用於建立 Application Context、Service Context static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { ContextImpl context = new ContextImpl(...); context.setResources(packageInfo.getResources()); return context; } private static Resources createResources(...) { return ResourcesManager.getInstance().getResources(...); } // ContextThemeWrapper 沒有重寫父類的 setResources // 所以會調用 mBase 的 setResources,即和 ContextImpl 的行爲同樣 void setResources(Resources r) { if (r instanceof CompatResources) { ((CompatResources) r).setContext(this); } mResources = r; } @Override public Resources getResources() { return mResources; } /* ---------- 主題相關 ------------ */ @Override public void setTheme(int resId) { synchronized (mSync) { if (mThemeResource != resId) { mThemeResource = resId; initializeTheme(); } } } // 直接建立一個 Themem 對象,相比 ContextThemeWrapper,少了一部份內容 private void initializeTheme() { if (mTheme == null) { mTheme = mResources.newTheme(); } mTheme.applyStyle(mThemeResource, true); } @Override public Resources.Theme getTheme() { synchronized (mSync) { // 和 ContextThemeWrapper 基本同樣 if (mTheme != null) { return mTheme; } mThemeResource = Resources.selectDefaultTheme(mThemeResource, getOuterContext().getApplicationInfo().targetSdkVersion); initializeTheme(); return mTheme; } } }
從代碼中能夠看出,ContextImpl 和 ContextThemeWrapper 最大的區別就是沒有一個 Configuration 而已,其它的行爲大體是同樣的。另外,ContextImpl 能夠用於建立 Activity、Service 以及 Application 的 mBase 成員,這個 Base Context 時除了參數不一樣,它們的 Resource 也不一樣。須要注意的是,createActivityContext 等方法中 setResource 是 mBase 本身調用的,Activity、Service 以及 Application 自己並無執行 setResource。
ContextWrapper、ContextThemeWrapper 都是 Context 的代理類,兩者的區別在於 ContextThemeWrapper 有本身的 Theme 以及 Resource,而且 Resource 能夠傳入本身的配置初始化
ContextImpl 是 Context 的主要實現類,Activity、Service 和 Application 的 Base Context 都是由它建立的,即 ContextWrapper 代理的就是 ContextImpl 對象自己
ContextImpl 和 ContextThemeWrapper 的主要區別是, ContextThemeWrapper 有 Configuration 對象,Resource 能夠根據這個對象來初始化
Service 和 Application 使用同一個 Recource,和 Activity 使用的 Resource 不一樣
先看 Activity,Activity 在啓動時,最終會執行 ActivityThread 的 performLaunchActivitiy:
public final class ActivityThread { private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... // 這個 Context 將會做爲 Activity 的 Base Context ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { ClassLoader cl = appContext.getClassLoader(); // 建立 Activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); } catch (Exception e) { ... } try { // 建立 Application Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { // 初始化 Activity,注意參數 appContext activity.attach(appContext, ...); ... } } catch (...) { ... } return activity; } private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { ContextImpl appContext = ContextImpl.createActivityContext(...); ... } }
能夠看到,Activity 的 Base Context 就是上面分析過的 ContextImpl 的 createActivityContext 建立的。
同時,Service 的 Base Context 的建立過程和 Application 同樣,調用的都是 ContextImpl 的 createAppContext,即 Service Context 和 Application Context 的 Resource 是相同的。所以這裏跳過 Service,下面看一下 Application Context。
在上面 ActivityThread 的 performLaunchActivity 方法中,能夠看到一個 makeApplication 的調用,它是 LoaedApk 的方法:
public final class LoadedApk { private Application mApplication; public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { return mApplication; } Application app = null; try { // 建立 Base Context ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); // 建立 Application 並設置 Base Context app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { ... } // Application 建立成功,賦值給 mApplication mApplication = app; ... return app; } // 獲取 mApplication Application getApplication() { return mApplication; } }
public class Application extends ContextWrapper implements ComponentCallbacks2 { /* package */ final void attach(Context context) { // 調用父類的 attachBaseContext 以設置 mBase attachBaseContext(context); } }
能夠看到,Instrumentation 是使用反射的方法建立 Application 對象,建立完畢後,會執行 Application 的 attach 方法設置 mBase 成員。
Application 及其 Base Context 的建立過程咱們瞭解了,接下來看一下 getApplicationContext 的實現:
class ContextImpl extends Context { ActivityThread mMainThread; LoadedApk mPackageInfo; @Override public Context getApplicationContext() { return (mPackageInfo != null) ? mPackageInfo.getApplication() : mMainThread.getApplication(); } }
從代碼中能夠看出,getApplicationContext 的返回值可能有兩個:第一個是 LoadedApk 的 getApplication 方法,這個方法的返回值就是剛剛建立的 Application 對象;第二個是 ActivityThread 的 getApplication 方法:
public final class ActivityThread { Application mInitialApplication; public Application getApplication() { return mInitialApplication; } public static ActivityThread systemMain() { // 建立 ActivityThread ActivityThread thread = new ActivityThread(); thread.attach(true); return thread; } private void attach(boolean system) { mSystemThread = system; if (!system) { ... } else { try { mInstrumentation = new Instrumentation(); // 注意參數 getSystemContext().mPackageInfo ContextImpl context = ContextImpl.createAppContext( this, getSystemContext().mPackageInfo); // 建立 Application mInitialApplication = context.mPackageInfo.makeApplication(true, null); mInitialApplication.onCreate(); } catch (Exception e) { ... } } ... } }
ActivityThread 中的 mInitialApplication 是在 systemMain 方法執行時建立的,而這個方法又是 SystemServer 啓動時調用的,結合參數 getSystemContext().mPackageInfo,所以我的推測 mInitialApplication 對應的是系統的某個 apk,即系統級別的 Application,但具體是否是這樣,目前尚未深刻研究過,有興趣的能夠本身研究。
通常狀況下,使用代理而不直接使用某個對象,目的可能有兩個:
其中 Servcie 和 Application 的父類 ContextWrapper 徹底沒有自定義的行爲,而 Activity 的父類 ContextThemeWrapper 則自定義了 Resource 以及 Theme 的相關行爲,所以,我的理解:
對於 Activity 的 getResource 問題,我寫了一份代碼來驗證:
public class MainActivity extends AppCompatActivity { private Configuration mOverrideConfiguration; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "getResources: " + getResources() + ", getBaseContext().getResources():" + getBaseContext().getResources()); } // 由於 Android 會在 onCreate 以前自動調用 getResource // 所以須要在這裏執行 applyOverrideConfiguration @Override public Resources getResources() { if (mOverrideConfiguration == null) { mOverrideConfiguration = new Configuration(); applyOverrideConfiguration(mOverrideConfiguration); } return super.getResources(); } }
輸出(我用的是小米手機):
getResources: android.content.res.MiuiResources@3c660a7, getBaseContext().getResources():android.content.res.MiuiResources@5143954
能夠看到,就像源碼顯示的那樣,應用了 Configuration 以後,Activity 的 getResource 方法返回的和 getBaseContext().getResources() 方法返回的不是同一個對象
Context 的繼承關係以下:
Context 至關於 Application 的大管家,主要負責:
ContextWrapper、ContextThemeWrapper、ContextImpl 的區別:
Activity Context、Service Context、Application Context、Base Context 的區別: