context繼承結(jié)構(gòu)
context的直接子類有2個,一個是ContextWrapper,一個是ContextImpl,那么從名字上就可以看出,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現(xiàn)類。而ContextWrapper又有三個直接的子類,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity。
==由此,其實我們就已經(jīng)可以得出結(jié)論了,Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各種承擔(dān)著不同的作用,但它們都屬于Context的一種,而它們具體Context的功能則是由ContextImpl類去實現(xiàn)的。==
那么context能做那些事情?
彈出Toast、啟動Activity、啟動Service、發(fā)送廣播、操作數(shù)據(jù)庫等等等等都需要用到Context。由于Context的具體能力是由ContextImpl類去實現(xiàn)的,因此在絕大多數(shù)場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出于安全原因的考慮,Android是不允許Activity或Dialog憑空出現(xiàn)的,一個Activity的啟動必須要建立在另一個Activity的基礎(chǔ)之上,也就是以此形成的返回棧。如何想要用不是activity的context打開activity的時候增加一個FLAG,因為打開一個activity需要一個棧我們指定一個棧就可以了。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場景下,我們只能使用Activity類型的Context,否則將會出錯。
Context數(shù)量
- Context一共有Application、Activity和Service三種類型,因此一個應(yīng)用程序中Context數(shù)量的計算公式就可以這樣寫:
Context數(shù)量 = Activity數(shù)量 + Service數(shù)量 + 1
上面的1代表著Application的數(shù)量,因為一個應(yīng)用程序中可以有多個Activity和多個Service,但是只能有一個Application。
Application Context的設(shè)計
- 我們獲取一個Application需要在xm里面注冊然后在activity里面調(diào)用
getApplication()
和getApplicationContext()
來獲取實例!我們通過測試得知這兩個獲取都是一個實例, - 那么為什么會有這兩個方法呢?
getApplication()方法的語義性非常強,一看就知道是用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調(diào)用的到。那么也許在絕大多數(shù)情況下我們都是在Activity或者Service中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的實例,這時就可以借助getApplicationContext()方法了,也就是說,getApplicationContext()方法的作用域會更廣一些,任何一個Context的實例,只要調(diào)用getApplicationContext()方法都可以拿到我們的Application對象。
- 我們發(fā)現(xiàn)還有一個getBaseContext()方法這個方法是獲取什么的呢?
getBaseContext()方法得到的是一個ContextImpl對象,也就是說像Application、Activity這樣的類其實并不會去具體實現(xiàn)Context的功能,而僅僅是做了一層接口封裝而已,Context的具體功能都是由ContextImpl類去完成的。
- 我們下源碼
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
@Override
public String getPackageName() {
return mBase.getPackageName();
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
}
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
@Override
public boolean stopService(Intent name) {
return mBase.stopService(name);
}
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
@Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
......
}
- 因為我們的activity和application都是直接或間接的繼承ContextWrapper,我們只需要看這個就行了,所以我們得知attachBaseContext()方法其實是由系統(tǒng)來調(diào)用的,它會把ContextImpl對象作為參數(shù)傳遞到attachBaseContext()方法當(dāng)中,從而賦值給mBase對象,之后ContextWrapper中的所有方法其實都是通過這種委托的機制交由ContextImpl去具體實現(xiàn)的,所以說ContextImpl是上下文功能的實現(xiàn)類是非常準(zhǔn)確的。
##使用Application的問題
public class MyApplication extends Application {
public MyApplication() {
String packageName = getPackageName();
Log.d("TAG", "package name is " + packageName);
}
}
這個就會報空指針而下面的寫法就沒有問題
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
String packageName = getPackageName();
Log.d("TAG", "package name is " + packageName);
}
}
- 這個是為什么呢?
我們前面不是說了,ContextWrapper中有一個attachBaseContext()方法,在這個里面才去初始化獲取到mBase對象才能調(diào)用它里面的方法!
##TIP
- 我們平時要盡量使用ApplicationContext不要使用activity的上下文,因為如果我們在activity里面進(jìn)行一些耗時操作,就會倒置當(dāng)前的activity沒辦法及時銷毀因為你持有當(dāng)前界面的context
- 博客原文[http://blog.csdn.net/guolin_blog/article/details/47028975](http://blog.csdn.net/guolin_blog/article/details/47028975)