Context的全面理解

本博客是一個(gè)純技術(shù)交流博客路召,寫(xiě)出來(lái)的文章是幫大家解決一些問(wèn)題讳嘱,或讓大家有個(gè)參考和思路,更多技術(shù)分享請(qǐng)關(guān)注http://blog.36dr.net虎敦,有任何問(wèn)題可與我郵件dr.kalen@yahoo.com游岳。

1. 什么是Context

在Android中Context分為ActivityContext和Application Context,Context是一個(gè)抽象類(lèi)是所有上下文的基類(lèi)其徙。

2. Activity Context 和Application Context 的區(qū)別

  • application context生命周期比較長(zhǎng)胚迫,伴隨應(yīng)用程序的存在而存在與activity的生命周期無(wú)關(guān),Activity Context 則只能存活于Activity 生命周期內(nèi)
  • 兩者的使用范圍和用于有部分差異唾那,具體可以查看圖示

數(shù)字1:?jiǎn)?dòng)Activity在這些類(lèi)中是可以的访锻,但是需要?jiǎng)?chuàng)建一個(gè)新的task。一般情況不推薦.
數(shù)字2:在這些類(lèi)中去layout inflate是合法的,但是會(huì)使用系統(tǒng)默認(rèn)的主題樣式期犬,如果你自定義了某些樣式可能不會(huì)被使用河哑。
數(shù)字3:在receiver為null時(shí)允許,在4.2或以上的版本中龟虎,用于獲取黏性廣播的當(dāng)前值璃谨。(可以無(wú)視)
注:ContentProvider、BroadcastReceiver之所以在上述表格中鲤妥,是因?yàn)樵谄鋬?nèi)部方法中都有一個(gè)context用于使用佳吞。

3. Context可以做什么

通過(guò)Context可以訪問(wèn)App全局信息的接口,例如:

  • 啟動(dòng)Activity棉安、Service容达、發(fā)送廣播
  • 訪問(wèn)APP中的資源和公開(kāi)的方法
  • 獲取assets中的資源
  • 對(duì)APK進(jìn)行管線管理

由以上來(lái)看,Context可以算是對(duì)APK無(wú)所不知垂券,可以操作資源花盐、代碼以及其他信息。

在創(chuàng)建Activity菇爪、Service算芯、Application時(shí)都會(huì)自動(dòng)創(chuàng)建Context,因此各自維護(hù)著自己的上下午凳宙,如果要統(tǒng)計(jì)一個(gè)app中的

Context數(shù)量=Activity數(shù)+Service數(shù)+application

如何使用Context做這些操作呢熙揍,我們舉例說(shuō)明:

  • 執(zhí)行APK中某類(lèi)的方法
Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
//載入這個(gè)類(lèi)
Class clazz = c.getClassLoader().loadClass("chroya.demo.Main");
//新建一個(gè)實(shí)例
Object owner = clazz.newInstance();
//獲取print方法,傳入?yún)?shù)并執(zhí)行
Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello”)氏涩;

ok届囚,這樣,我們就調(diào)用了chroya.demo包的Main類(lèi)的print方法是尖,執(zhí)行結(jié)果意系。其中需要對(duì)createPackageContext的參數(shù)說(shuō)明
1、packageName 包名饺汹,要得到Context的包名
2蛔添、flags 標(biāo)志位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY兩個(gè)選項(xiàng)兜辞。CONTEXT_INCLUDE_CODE的意思是包括代碼迎瞧,也就是說(shuō)可以執(zhí)行這個(gè)包里面的代碼。
CONTEXT_IGNORE_SECURITY的意思是忽略安全警告逸吵,如果不加這個(gè)標(biāo)志的話凶硅,有些功能是用不了的,會(huì)出現(xiàn)安全警告扫皱。

  • 訪問(wèn)apk中的資源

在ContextImpl中存在一個(gè)mResources變量足绅,變量值的來(lái)源如下代碼捷绑,它就是我們?cè)谕ㄟ^(guò)Context.getResource()方法返回的結(jié)果,而 resource中存在一個(gè)ArrayMap用于存儲(chǔ)所有的資源對(duì)象编检,因?yàn)橐粋€(gè)app針對(duì)不同分辨率胎食、不同方向等擁有多套資源,因此ArrayMap中會(huì)是多個(gè)對(duì)象允懂,又ResourceManager使用的單例模式厕怜,因此每個(gè)Activity使用的是同一套資源但不一定是同一個(gè)資源對(duì)象。

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

4. Context造成內(nèi)存泄露

通常造成Context內(nèi)存泄露的原因是因?yàn)槔僮埽到y(tǒng)gc執(zhí)行時(shí)無(wú)法銷(xiāo)毀Context粥航,我們舉例說(shuō)明:

我們都知道應(yīng)用當(dāng)屏幕旋轉(zhuǎn)時(shí)會(huì)銷(xiāo)毀當(dāng)前Activity然后重新創(chuàng)建一個(gè)新的Activity,當(dāng)我們activity中有圖片加載時(shí)而我們又希望保持這個(gè)圖片不重新加載生百,我們可能采用一個(gè)靜態(tài)變量來(lái)保持這個(gè)Drawable递雀,此時(shí)當(dāng)屏幕旋轉(zhuǎn)的時(shí)候由于Drawable與View有關(guān)聯(lián),Drawable保存了view的引用蚀浆,而view保存了Activity引用缀程,因此導(dǎo)致在旋轉(zhuǎn)屏幕是無(wú)法將activity銷(xiāo)毀,而造成內(nèi)存泄露市俊,程序崩潰杨凑。

<font color="#FF1493">防止Context導(dǎo)致內(nèi)存泄露,我們應(yīng)該注意保存Activity中的對(duì)象與Activity是同一個(gè)生命周期摆昧,對(duì)已需要非常長(zhǎng)的周期對(duì)象可以采用Application Context撩满。</font>

5. Unable to add window — token null is not for an application

此錯(cuò)誤一般是在彈出框時(shí)出現(xiàn)異常如:

 Dialog dialog = new Dialog(getApplicationContext());

或者

   Dialog dialog = new Dialog(getApplication());

由于dialog是一個(gè)窗口,因此需要一個(gè)擁有窗口令牌的Token绅你,而在packageInfo存在的情況下getApplication與getApplication返回的是同一個(gè)application context伺帘,application context并沒(méi)有窗口令牌,因此會(huì)出現(xiàn)這種異常忌锯,正確的做法:

   AlertDialog.Builder builder = new AlertDialog.Builder(this);

[1] 具體創(chuàng)建時(shí)代碼:

創(chuàng)建Application時(shí):創(chuàng)建Application的時(shí)機(jī)在創(chuàng)建handleBindApplication()方法
//創(chuàng)建Application時(shí)同時(shí)創(chuàng)建的ContextIml實(shí)例

 2 private final void handleBindApplication(AppBindData data){
 3     …
 4     ///創(chuàng)建Application對(duì)象
 5     Application app = data.info.makeApplication(data.restrictedBackupMode, null);
 6     …
 7 }
 8 public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
 9     …
10     try {
11         java.lang.ClassLoader cl = getClassLoader();
12         ContextImpl appContext = new ContextImpl();    //創(chuàng)建一個(gè)ContextImpl對(duì)象實(shí)例
13         appContext.init(this, null, mActivityThread);  //初始化該ContextIml實(shí)例的相關(guān)屬性
14         ///新建一個(gè)Application對(duì)象
15         app = mActivityThread.mInstrumentation.newApplication(
16                 cl, appClass, appContext);
17        appContext.setOuterContext(app);  //將該Application實(shí)例傳遞給該ContextImpl實(shí)例
18     }
19     …
20 }

創(chuàng)建Activity時(shí)通過(guò)startActivity()或startActivityForResult()請(qǐng)求啟動(dòng)一個(gè)Activity時(shí)伪嫁,如果系統(tǒng)檢測(cè)需要新建一個(gè)Activity對(duì)象時(shí)回調(diào)handleLaunchActivity()方法。
//創(chuàng)建一個(gè)Activity實(shí)例時(shí)同時(shí)創(chuàng)建ContextIml實(shí)例

private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity a = performLaunchActivity(r, customIntent);  //啟動(dòng)一個(gè)Activity
}
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity activity = null;
    try {
        //創(chuàng)建一個(gè)Activity對(duì)象實(shí)例
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    }
    if (activity != null) {
        ContextImpl appContext = new ContextImpl();      //創(chuàng)建一個(gè)Activity實(shí)例
        appContext.init(r.packageInfo, r.token, this);   //初始化該ContextIml實(shí)例的相關(guān)屬性
        appContext.setOuterContext(activity);            //將該Activity信息傳遞給該ContextImpl實(shí)例
        …
    }
    …
}

創(chuàng)建Service時(shí)通過(guò)startService或者bindService時(shí)汉规,如果系統(tǒng)檢測(cè)到需要新創(chuàng)建一個(gè)Service實(shí)例礼殊,就會(huì)回調(diào)handleCreateService()方法
//創(chuàng)建一個(gè)Service實(shí)例時(shí)同時(shí)創(chuàng)建ContextIml實(shí)例

private final void handleCreateService(CreateServiceData data){
   …
   //創(chuàng)建一個(gè)Service實(shí)例
   Service service = null;
   try {
       java.lang.ClassLoader cl = packageInfo.getClassLoader();
       service = (Service) cl.loadClass(data.info.name).newInstance();
   } catch (Exception e) {
   }
   …
   ContextImpl context = new ContextImpl(); //創(chuàng)建一個(gè)ContextImpl對(duì)象實(shí)例
   context.init(packageInfo, null, this);   //初始化該ContextIml實(shí)例的相關(guān)屬性
   //獲得我們之前創(chuàng)建的Application對(duì)象信息
   Application app = packageInfo.makeApplication(false, mInstrumentation);
   //將該Service信息傳遞給該ContextImpl實(shí)例
   context.setOuterContext(service);
   …
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市针史,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碟狞,老刑警劉巖啄枕,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異族沃,居然都是意外死亡频祝,警方通過(guò)查閱死者的電腦和手機(jī)泌参,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)常空,“玉大人沽一,你說(shuō)我怎么就攤上這事±觳冢” “怎么了铣缠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)昆禽。 經(jīng)常有香客問(wèn)我蝗蛙,道長(zhǎng),這世上最難降的妖魔是什么醉鳖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任捡硅,我火速辦了婚禮,結(jié)果婚禮上盗棵,老公的妹妹穿的比我還像新娘壮韭。我一直安慰自己,他們只是感情好纹因,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布喷屋。 她就那樣靜靜地躺著,像睡著了一般辐怕。 火紅的嫁衣襯著肌膚如雪逼蒙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天寄疏,我揣著相機(jī)與錄音是牢,去河邊找鬼。 笑死陕截,一個(gè)胖子當(dāng)著我的面吹牛驳棱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播农曲,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼社搅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了乳规?” 一聲冷哼從身側(cè)響起形葬,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暮的,沒(méi)想到半個(gè)月后笙以,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冻辩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年猖腕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拆祈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倘感,死狀恐怖放坏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情老玛,我是刑警寧澤淤年,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站逻炊,受9級(jí)特大地震影響互亮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜余素,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一豹休、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桨吊,春花似錦威根、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至佑淀,卻和暖如春留美,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伸刃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工谎砾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捧颅。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓景图,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親碉哑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挚币,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容