国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Context詳解

2019-11-15 00:37:08
字體:
供稿:網(wǎng)友
Context詳解前言

Context在android中的作用不言而喻,當(dāng)我們訪問當(dāng)前應(yīng)用的資源,啟動一個新的activity的時候都需要提供Context,而這個Context到底是什么呢,這個問題好像很好回答又好像難以說清楚。從字面意思,Context的意思是“上下文”,或者也可以叫做環(huán)境、場景等,盡管如此,還是有點抽象。從類的繼承來說,Context作為一個抽象的基類,它的實現(xiàn)子類有三種:application、Activity和Service(估計這么說,暫時不管ContextWrapper等類),那么這三種有沒有區(qū)別呢?為什么通過任意的Context訪問資源都得到的是同一套資源呢?getApplication和getApplicationContext有什么區(qū)別呢?應(yīng)用中到底有多少個Context呢?本文將圍繞這些問題一一展開,所用源碼版本為Android4.4。

什么是Context

Context是一個抽象基類,我們通過它訪問當(dāng)前包的資源(getResources、getAssets)和啟動其他組件(Activity、Service、Broadcast)以及得到各種服務(wù)(getSystemService),當(dāng)然,通過Context能得到的不僅僅只有上述這些內(nèi)容。對Context的理解可以來說:Context提供了一個應(yīng)用的運行環(huán)境,在Context的大環(huán)境里,應(yīng)用才可以訪問資源,才能完成和其他組件、服務(wù)的交互,Context定義了一套基本的功能接口,我們可以理解為一套規(guī)范,而Activity和Service是實現(xiàn)這套規(guī)范的子類,這么說也許并不準(zhǔn)確,因為這套規(guī)范實際是被ContextImpl類統(tǒng)一實現(xiàn)的,Activity和Service只是繼承并有選擇性地重寫了某些規(guī)范的實現(xiàn)。

Application、Activity和Service作為Context的區(qū)別

首先,它們都間接繼承了Context,這是它們的相同點。

不同點,可以從幾個方面來說:首先看它們的繼承關(guān)系

Activity的繼承關(guān)系

Service和Application的繼承關(guān)系

通過對比可以清晰地發(fā)現(xiàn),Service和Application的類繼承關(guān)系比較像,而Activity還多了一層繼承ContextThemeWrapper,這是因為Activity有主題的概念,而Service是沒有界面的服務(wù),Application更是一個抽象的東西,它也是通過Activity類呈現(xiàn)的。

下面來看一下三者在Context方面的區(qū)別

上文已經(jīng)指出,Context的真正實現(xiàn)都在ContextImpl中,也就是說Context的大部分方法調(diào)用都會轉(zhuǎn)到ContextImpl中,而三者的創(chuàng)建均在ActivityThread中完成,我之前寫過一篇文章Android源碼分析-Activity的啟動過程,在文中我指出Activity啟動的核心過程是在ActivityThread中完成的,這里要說明的是,Application和Service的創(chuàng)建也是在ActivityThread中完成的。下面我們看下三者在創(chuàng)建時是怎么和ContextImpl相關(guān)聯(lián)的。

Activity對象中ContextImpl的創(chuàng)建

代碼為ActivityThread中的performLaunchActivity方法

  1. if(activity!=null){
  2. ContextappContext=createBaseContextForActivity(r,activity);
  3. /**
  4. *createBaseContextForActivity中創(chuàng)建ContextImpl的代碼
  5. *ContextImplappContext=newContextImpl();
  6. *appContext.init(r.packageInfo,r.token,this);
  7. *appContext.setOuterContext(activity);
  8. */
  9. CharSequencetitle=r.activityInfo.loadLabel(appContext.getPackageManager());
  10. Configurationconfig=newConfiguration(mCompatConfiguration);
  11. if(DEBUG_CONFIGURATION)Slog.v(TAG,"Launchingactivity"
  12. +r.activityInfo.name+"withconfig"+config);
  13. activity.attach(appContext,this,getInstrumentation(),r.token,
  14. r.ident,app,r.intent,r.activityInfo,title,r.parent,
  15. r.embeddedID,r.lastNonConfigurationInstances,config);
  16. if(customIntent!=null){
  17. activity.mIntent=customIntent;
  18. }
  19. ...
  20. }

可以看出,Activity在創(chuàng)建的時候會new一個ContextImpl對象并在attach方法中關(guān)聯(lián)它,需要注意的是,創(chuàng)建Activity使用的數(shù)據(jù)結(jié)構(gòu)是ActivityClientRecord。

Application對象中ContextImpl的創(chuàng)建

代碼在ActivityThread中的handleBindApplication方法中,此方法內(nèi)部調(diào)用了makeApplication方法

  1. publicApplicationmakeApplication(booleanforceDefaultAppClass,
  2. Instrumentationinstrumentation){
  3. if(mApplication!=null){
  4. returnmApplication;
  5. }
  6. Applicationapp=null;
  7. StringappClass=mApplicationInfo.className;
  8. if(forceDefaultAppClass||(appClass==null)){
  9. appClass="android.app.Application";
  10. }
  11. try{
  12. java.lang.ClassLoadercl=getClassLoader();
  13. ContextImplappContext=newContextImpl();
  14. appContext.init(this,null,mActivityThread);
  15. app=mActivityThread.mInstrumentation.newApplication(
  16. cl,appClass,appContext);
  17. appContext.setOuterContext(app);
  18. }catch(Exceptione){
  19. if(!mActivityThread.mInstrumentation.onException(app,e)){
  20. thrownewRuntimeException(
  21. "Unabletoinstantiateapplication"+appClass
  22. +":"+e.toString(),e);
  23. }
  24. }
  25. ...
  26. }

看代碼發(fā)現(xiàn)和Activity中ContextImpl的創(chuàng)建是相同的。

Service對象中ContextImpl的創(chuàng)建

通過查看代碼發(fā)現(xiàn)和Activity、Application是一致的。分析到這里,那么三者的Context有什么區(qū)別呢?沒有區(qū)別嗎?盡管如此,有一些細(xì)節(jié)是確定的:Dialog的使用需要Activity,在桌面上我們采用Application的Context無法彈出對話框,同時在桌面上想啟動新的activity,我們需要為intent設(shè)置FLAG_ACTIVITY_NEW_TASK標(biāo)志,否則無法啟動activity,這一切都說明,起碼Application的Context和Activity的Context還是有區(qū)別的,當(dāng)然這也可能不是Context的區(qū)別,因為在桌面上,我們的應(yīng)用沒有界面,這意味著我們能干的事情可能受到了限制,事情的細(xì)節(jié)目前我還沒有搞的很清楚。

Context對資源的訪問

很明確,不同的Context得到的都是同一份資源。這是很好理解的,請看下面的分析

得到資源的方式為context.getResources,而真正的實現(xiàn)位于ContextImpl中的getResources方法,在ContextImpl中有一個成員PRivate Resources mResources,它就是getResources方法返回的結(jié)果,mResources的賦值代碼為:

mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);

下面看一下ResourcesManager的getTopLevelResources方法,這個方法的思想是這樣的:在ResourcesManager中,所有的資源對象都被存儲在ArrayMap中,首先根據(jù)當(dāng)前的請求參數(shù)去查找資源,如果找到了就返回,否則就創(chuàng)建一個資源對象放到ArrayMap中。有一點需要說明的是為什么會有多個資源對象,原因很簡單,因為res下可能存在多個適配不同設(shè)備、不同分辨率、不同系統(tǒng)版本的目錄,按照android系統(tǒng)的設(shè)計,不同設(shè)備在訪問同一個應(yīng)用的時候訪問的資源可以不同,比如drawable-hdpi和drawable-xhdpi就是典型的例子。

  1. publicResourcesgetTopLevelResources(StringresDir,intdisplayId,
  2. ConfigurationoverrideConfiguration,CompatibilityInfocompatInfo,IBindertoken){
  3. finalfloatscale=compatInfo.applicationScale;
  4. ResourcesKeykey=newResourcesKey(resDir,displayId,overrideConfiguration,scale,
  5. token);
  6. Resourcesr;
  7. synchronized(this){
  8. //Resourcesisappscaledependent.
  9. if(false){
  10. Slog.w(TAG,"getTopLevelResources:"+resDir+"/"+scale);
  11. }
  12. WeakReference<Resources>wr=mActiveResources.get(key);
  13. r=wr!=null?wr.get():null;
  14. //if(r!=null)Slog.i(TAG,"isUpToDate"+resDir+":"+r.getAssets().isUpToDate());
  15. if(r!=null&&r.getAssets().isUpToDate()){
  16. if(false){
  17. Slog.w(TAG,"Returningcachedresources"+r+""+resDir
  18. +":appScale="+r.getCompatibilityInfo().applicationScale);
  19. }
  20. returnr;
  21. }
  22. }
  23. //if(r!=null){
  24. //Slog.w(TAG,"Throwingawayout-of-dateresources!!!!"
  25. //+r+""+resDir);
  26. //}
  27. AssetManagerassets=newAssetManager();
  28. if(assets.addAssetPath(resDir)==0){
  29. returnnull;
  30. }
  31. //Slog.i(TAG,"Resource:key="+key+",displaymetrics="+metrics);
  32. DisplayMetricsdm=getDisplayMetricsLocked(displayId);
  33. Configurationconfig;
  34. booleanisDefaultDisplay=(displayId==Display.DEFAULT_DISPLAY);
  35. finalbooleanhasOverrideConfig=key.hasOverrideConfiguration();
  36. if(!isDefaultDisplay||hasOverrideConfig){
  37. config=newConfiguration(getConfiguration());
  38. if(!isDefaultDisplay){
  39. applyNonDefaultDisplayMetricsToConfigurationLocked(dm,config);
  40. }
  41. if(hasOverrideConfig){
  42. config.updateFrom(key.mOverrideConfiguration);
  43. }
  44. }else{
  45. config=getConfiguration();
  46. }
  47. r=newResources(assets,dm,config,compatInfo,token);
  48. if(false){
  49. Slog.i(TAG,"Createdappresources"+resDir+""+r+":"
  50. +r.getConfiguration()+"appScale="
  51. +r.getCompatibilityInfo().applicationScale);
  52. }
  53. synchronized(this){
  54. WeakReference<Resources>wr=mActiveResources.get(key);
  55. Resourcesexisting=wr!=null?wr.get():null;
  56. if(existing!=null&&existing.getAssets().isUpToDate()){
  57. //Someoneelsealreadycreatedtheresourceswhilewewere
  58. //unlocked;goaheadandusetheirs.
  59. r.getAssets().close();
  60. returnexisting;
  61. }
  62. //XXXneedtoremoveentrieswhenweakreferencesgoaway
  63. mActiveResources.put(key,newWeakReference<Resources>(r));
  64. returnr;
  65. }
  66. }

根據(jù)上述代碼中資源的請求機制,再加上ResourcesManager采用單例模式,這樣就保證了不同的ContextImpl訪問的是同一套資源,注意,這里說的同一套資源未必是同一個資源,因為資源可能位于不同的目錄,但它一定是我們的應(yīng)用的資源,或許這樣來描述更準(zhǔn)確,在設(shè)備參數(shù)和顯示參數(shù)不變的情況下,不同的ContextImpl訪問到的是同一份資源。設(shè)備參數(shù)不變是指手機的屏幕和android版本不變,顯示參數(shù)不變是指手機的分辨率和橫豎屏狀態(tài)。也就是說,盡管Application、Activity、Service都有自己的ContextImpl,并且每個ContextImpl都有自己的mResources成員,但是由于它們的mResources成員都來自于唯一的ResourcesManager實例,所以它們看似不同的mResources其實都指向的是同一塊內(nèi)存(C語言的概念),因此,它們的mResources都是同一個對象(在設(shè)備參數(shù)和顯示參數(shù)不變的情況下)。在橫豎屏切換的情況下且應(yīng)用中為橫豎屏狀態(tài)提供了不同的資源,處在橫屏狀態(tài)下的ContextImpl和處在豎屏狀態(tài)下的ContextImpl訪問的資源不是同一個資源對象。

代碼:單例模式的ResourcesManager類

  1. publicstaticResourcesManagergetInstance(){
  2. synchronized(ResourcesManager.class){
  3. if(sResourcesManager==null){
  4. sResourcesManager=newResourcesManager();
  5. }
  6. returnsResourcesManager;
  7. }
  8. }
getApplication和getApplicationContext的區(qū)別

getApplication返回結(jié)果為Application,且不同的Activity和Service返回的Application均為同一個全局對象,在ActivityThread內(nèi)部有一個列表專門用于維護所有應(yīng)用的application

final ArrayList<Application> mAllApplications = new ArrayList<Application>();

getApplicationContext返回的也是Application對象,只不過返回類型為Context,看看它的實現(xiàn)

  1. @Override
  2. publicContextgetApplicationContext(){
  3. return(mPackageInfo!=null)?
  4. mPackageInfo.getApplication():mMainThread.getApplication();
  5. }

上面代碼中mPackageInfo是包含當(dāng)前應(yīng)用的包信息、比如包名、應(yīng)用的安裝目錄等,原則上來說,作為第三方應(yīng)用,包信息mPackageInfo不可能為空,在這種情況下,getApplicationContext返回的對象和getApplication是同一個。但是對于系統(tǒng)應(yīng)用,包信息有可能為空,具體就不深入研究了。從這種角度來說,對于第三方應(yīng)用,一個應(yīng)用只存在一個Application對象,且通過getApplication和getApplicationContext得到的是同一個對象,兩者的區(qū)別僅僅是返回類型不同。

應(yīng)用中Context的數(shù)量

到此已經(jīng)很明了了,一個應(yīng)用中Context的數(shù)量等于Activity的個數(shù)+ Service的個數(shù)+ 1,這個1為Application。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 铜陵市| 屯昌县| 尼勒克县| 景宁| 辽宁省| 香河县| 玉门市| 都兰县| 浦北县| 涿鹿县| 南昌市| 兴文县| 巴彦淖尔市| 黄骅市| 周至县| 韩城市| 大理市| 台江县| 驻马店市| 龙门县| 衡阳市| 泰安市| 永泰县| 略阳县| 黄浦区| 南京市| 新邵县| 湘阴县| 潼南县| 兰坪| 恩施市| 安岳县| 贡山| 新津县| 梅河口市| 柘城县| 霍林郭勒市| 晋宁县| 离岛区| 忻州市| 鄂尔多斯市|