如需轉載請評論或簡信,并注明出處,未經允許不得轉載
目錄
前言
在Android應用進程的創建 — Activity的啟動流程中我們發現,Application
和Activity
都是由系統創建的,它們并不能像其他java類一樣,由應用層通過new操作創建出對象,且它們都繼承自Context
,那么大家有沒有想過,這個Context
到底有什么作用呢?它在Android應用中扮演了一個怎么樣的角色呢?
Context繼承關系
這里整理了一張Context
的繼承關系類圖,從這個圖中可以看出,Context
是一個接口,ContextImp
和ContextWrapper
都是其實現類,我們常用的Activity
、Service
、Application
都直接或間接繼承自ContextWrapper
通過這張圖,我們可以整理出幾個問題:
一個應用程序有幾個
Context
?為什么
Activity
、Service
、Application
都繼承自Context
,Context
的作用是什么呢?為什么
Activity
需要繼承自ContextThemeWrapper
,而Service
和Application
直接繼承自ContextWrapper
呢?為什么
ContextWrapper
中存在一個ContextImp
類型的變量mBase
,且同時它又實現了Context
呢?
問題分析
問題一
一個應用程序有幾個Context?
Application
,Activity
,Service
都繼承自Context
,而應用有幾個進程,就會存在幾個Application對象
Context個數 = Activity個數 + Service個數 + 進程個數
問題二
為什么Activity、Service、Application都繼承自Context,Context的作用是什么呢?
Context是一個接口,所以要想知道Context的作用,其實就是看它有哪些接口,這些接口的功能是什么
/**
* 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
就像是應用的大管家,正是因為有了Context
,各種應用組件才有意義,他們才能訪問系統服務,系統資源
Context
的作用總結為如下幾個方面:
- 四大組件的交互,包括啟動
Activity
、Broadcast
、Service
,獲取ContentResolver
等 - 獲取系統/應用資源,包括
AssetManager
、PackageManager
、Resources
、System Service
以及color
、string
、drawable
等 - 文件,包括獲取緩存文件夾、刪除文件、
SharedPreference
相關等 - 數據庫(
SQLite
)相關,包括打開數據庫、刪除數據庫、獲取數據庫路徑等 - 其它輔助功能,比如設置
ComponentCallbacks
,即監聽配置信息改變、內存不足等事件的發生
問題三
為什么Activity需要繼承自ContextThemeWrapper,而Service和Application直接繼承自ContextWrapper呢?
下面來看一下ContextThemeWrapper
的源碼
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);
}
public void applyOverrideConfiguration(Configuration overrideConfiguration) {...}
public Configuration getOverrideConfiguration() {...}
@Override
public AssetManager getAssets() {...}
@Override
public Resources getResources() {...}
private Resources getResourcesInternal() {...}
@Override
public void setTheme(int resid) {...}
@Override
public int getThemeResId() {...}
@Override
public Resources.Theme getTheme() {...}
@Override
public Object getSystemService(String name) {...}
protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
private void initializeTheme() {...}
}
ContextThemeWrapper
類,從它的命名就可以看出,其內部包含了與Theme
相關的接口,當然,只有Activity才需要主題,Service
和Application
是不需要主題的,因為Service
是沒有界面的后臺場景,所以Service
和Application
直接繼承于ContextWrapper
問題四
為什么ContextWrapper中存在一個ContextImp類型的變量mBase,且同時它又實現了ContextWrapper呢?
下面來看ContextWrapper
的代碼
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();
}
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
....
}
很顯然,ContextWrapper
只是一個Context
靜態代理類,所有的操作都是通過內部成員 mBase
完成的,而mBase
就是ContextImp
對象。為什么要這么設計呢?如果Application
直接繼承ContextImp
會不會有什么問題呢?
一般情況下,使用代理而不直接使用某個對象,目的可能有兩個:
- 定制自己的行為
- 不影響原對象
其中 Servcie
和 Application
的父類 ContextWrapper 完全沒有自定義的行為,而 Activity 的父類 ContextThemeWrapper
則自定義了 Resource
以及 Theme
的相關行為,因此:
- 對于
Service
和Application
而言,不直接繼承ContextImp
,是擔心用戶修改了ContextImp
而導致錯誤的發生 - 對于 Activity 而言,除了擔心用戶的修改之外,
ContextImp
和Activity
本身對于Reource
以及Theme
的相關行為是不同的
其他使用Context注意點
- 在單例模式中使用Context要注意內存泄漏問題
我們知道,單例模式的生命周期是和應用的生命周期保持一致的,所以在單例模式中使用Context
,不能使用Activity Context
,需要使用Application Context
- 創建dialog需要使用Activtiy Context
果我們使用Application的Context,或者說Token可以不是Activity的Token,那么用戶可能已經跳轉到別的應用的Activity界面了,但我們卻可以在別人的界面上彈出我們的Dialog,想想就覺得很危險
具體可以參考:http://www.lxweimin.com/p/628ac6b68c15
- Activity的this和getBaseContext()有什么區別?
Activity
就是繼承Context
的,所以this
是返回Activity
自己
getBaseContext()
返回的是ContextWrapper
里面的mBase
- getApplication()和getApplicationContext()有什么區別?
都是返回Applicatoin
對象,但是他們的作用域不一樣
getApplicatoin()
是Activity
和Service
里面特有的,其他地方不能用
BroadcastReceiver
的onReceive()
中的第一個參數,拿到的Context對象是不能調用getApplication()
的只能調getApplicationContext()
- Application構造方法中調用父類方法會發生閃退(如getResource()等)
Application
的創建過程主要是下面三步
//1.創建ContextImpl
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//2.創建Application
Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
//3.調用Application#onCreate()
app.onCreate();
Instrumentation#newApplication()
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
Application#attach()
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
Application#attachBaseContext()
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
從上面的代碼中可以看出,在Application
對象剛被創建的時候,其內部的mBase
變量是空的,直到執行attachBaseContext()
后,mBase
才會有值,之后才會調用Application#onCreate()
。所以在Application
中使用Context
接口中的相關方法,可以在onCreate()
里面調用,而不能在構造方法中調用
總結
Context
是一個抽象類,我們開發過程中幾乎每天都要和它打交道,但是我相信很多人都說不出他是個什么東西,很多只是知道它叫做上下文。其實當我們覺得一個東西很抽象很難理解的時候,無外乎就是看一下它的創建過程以及它具備哪些方法
這里做一個比喻,對于我們應用層開發來說,Activity
就像是一個”皇帝“,Activity
可以做很多很多的事情,但是Context
就像是他手中的權利,如果沒有Context
,Activity
其實只是一個”普通人“(普通java類)而已
我們很多人往往把Activity理解成它繼承了Context,是的沒錯,它確實繼承自Context,但我認為,把Activity理解成它代理了Context,會更貼合實際意義一些