本博客是一個(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);
…
}