Context的全面理解

本博客是一个纯技术交流博客,写出来的文章是帮大家解决一些问题,或让大家有个参考和思路,更多技术分享请关注http://blog.36dr.net,有任何问题可与我邮件dr.kalen@yahoo.com

1. 什么是Context

在Android中Context分为ActivityContext和Application Context,Context是一个抽象类是所有上下文的基类。

2. Activity Context 和Application Context 的区别

  • application context生命周期比较长,伴随应用程序的存在而存在与activity的生命周期无关,Activity Context 则只能存活于Activity 生命周期内
  • 两者的使用范围和用于有部分差异,具体可以查看图示

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐.
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。

3. Context可以做什么

通过Context可以访问App全局信息的接口,例如:

  • 启动Activity、Service、发送广播
  • 访问APP中的资源和公开的方法
  • 获取assets中的资源
  • 对APK进行管线管理

由以上来看,Context可以算是对APK无所不知,可以操作资源、代码以及其他信息。

在创建Activity、Service、Application时都会自动创建Context,因此各自维护着自己的上下午,如果要统计一个app中的

Context数量=Activity数+Service数+application

如何使用Context做这些操作呢,我们举例说明:

  • 执行APK中某类的方法
Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
//载入这个类
Class clazz = c.getClassLoader().loadClass("chroya.demo.Main");
//新建一个实例
Object owner = clazz.newInstance();
//获取print方法,传入参数并执行
Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello”);

ok,这样,我们就调用了chroya.demo包的Main类的print方法,执行结果。其中需要对createPackageContext的参数说明
1、packageName 包名,要得到Context的包名
2、flags 标志位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项。CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。
CONTEXT_IGNORE_SECURITY的意思是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。

  • 访问apk中的资源

在ContextImpl中存在一个mResources变量,变量值的来源如下代码,它就是我们在通过Context.getResource()方法返回的结果,而 resource中存在一个ArrayMap用于存储所有的资源对象,因为一个app针对不同分辨率、不同方向等拥有多套资源,因此ArrayMap中会是多个对象,又ResourceManager使用的单例模式,因此每个Activity使用的是同一套资源但不一定是同一个资源对象。

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

4. Context造成内存泄露

通常造成Context内存泄露的原因是因为,系统gc执行时无法销毁Context,我们举例说明:

我们都知道应用当屏幕旋转时会销毁当前Activity然后重新创建一个新的Activity,当我们activity中有图片加载时而我们又希望保持这个图片不重新加载,我们可能采用一个静态变量来保持这个Drawable,此时当屏幕旋转的时候由于Drawable与View有关联,Drawable保存了view的引用,而view保存了Activity引用,因此导致在旋转屏幕是无法将activity销毁,而造成内存泄露,程序崩溃。

<font color="#FF1493">防止Context导致内存泄露,我们应该注意保存Activity中的对象与Activity是同一个生命周期,对已需要非常长的周期对象可以采用Application Context。</font>

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

此错误一般是在弹出框时出现异常如:

 Dialog dialog = new Dialog(getApplicationContext());

或者

   Dialog dialog = new Dialog(getApplication());

由于dialog是一个窗口,因此需要一个拥有窗口令牌的Token,而在packageInfo存在的情况下getApplication与getApplication返回的是同一个application context,application context并没有窗口令牌,因此会出现这种异常,正确的做法:

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

[1] 具体创建时代码:

创建Application时:创建Application的时机在创建handleBindApplication()方法
//创建Application时同时创建的ContextIml实例

 2 private final void handleBindApplication(AppBindData data){
 3     …
 4     ///创建Application对象
 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();    //创建一个ContextImpl对象实例
13         appContext.init(this, null, mActivityThread);  //初始化该ContextIml实例的相关属性
14         ///新建一个Application对象
15         app = mActivityThread.mInstrumentation.newApplication(
16                 cl, appClass, appContext);
17        appContext.setOuterContext(app);  //将该Application实例传递给该ContextImpl实例
18     }
19     …
20 }

创建Activity时通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时回调handleLaunchActivity()方法。
//创建一个Activity实例时同时创建ContextIml实例

private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity a = performLaunchActivity(r, customIntent);  //启动一个Activity
}
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity activity = null;
    try {
        //创建一个Activity对象实例
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    }
    if (activity != null) {
        ContextImpl appContext = new ContextImpl();      //创建一个Activity实例
        appContext.init(r.packageInfo, r.token, this);   //初始化该ContextIml实例的相关属性
        appContext.setOuterContext(activity);            //将该Activity信息传递给该ContextImpl实例
        …
    }
    …
}

创建Service时通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法
//创建一个Service实例时同时创建ContextIml实例

private final void handleCreateService(CreateServiceData data){
   …
   //创建一个Service实例
   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(); //创建一个ContextImpl对象实例
   context.init(packageInfo, null, this);   //初始化该ContextIml实例的相关属性
   //获得我们之前创建的Application对象信息
   Application app = packageInfo.makeApplication(false, mInstrumentation);
   //将该Service信息传递给该ContextImpl实例
   context.setOuterContext(service);
   …
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容