Context, What Context?

来源:互联网 发布:淘宝卖家15天资金锁住 编辑:程序博客网 时间:2024/06/14 23:03

请点击查看原文


Context可能是Android应用程序中最常用的元素…同时也最容易被误用.

Context对象非常普遍和常用,它可能会发生出人预料的情形. 加载资源、启动Activity、获得系统服务、获取内部文件路径、创建View等等都需要Context的支持才能完成. 接下来我会为你提供一些和Context工作原理相关的说明,希望能帮助你更有效的开发.

Context类型

Context实例的创建方式不是唯一的. 根据Android组件的不同,创建出的Context实例是有细微区别的.

Application

Context在APP中以单例模式存在. 可以通过不同的方式得到Context实例,如在Activity或Service中的getApplication()、或者从其他继承了Context的任何子类调用getApplicationContext()获取. 无论从何处以何种方式得到,Context在一个APP的所有进程中是只有唯一实例.

Activity/Service

继承自ContextWrapper并实现了相同的API,代理了Context实例的所有内部隐藏的回调方法. 当Framework层创建新的Activity或Service实例时,同样会创建ContentImpl实例来完成其他组件内部的重型任务. 任何Activity和Service以及他们之间的通信都基于在每个进程中只有唯一实例的Context对象.

BroadcastReceiver

本身没有Context实例,但是Framework层会在在每次广播事件发生时的onReceive()方法中传递Context实例进来. 该实例是受限制的,有两个主要功能(registerReceiver()bindService())被阉割了. 这两个功能不能从现有的BroadcastReceiver.onReceive()调用. 每次接受者处理广播,Context会传递进来一个新的实例.

ContentProvider

本身没有Context实例,不过可以通过getContext()获取. 如果ContentProvider运行在本应用程序内,则会获取Application单例. 如果该ContentProvider与调用者不在同一进程,则会创建新的实例代表provider所运行的包.

保存引用

首要解决的问题是Context的生命周期可能会比其所在的对象的生命周期要长. 例如,创建一个自定义单例,其需要Context对象加载资源或者获取ContentProvider,并保存引用的Activity和Service的单例.


非良好单例

 public class CustomManager {            private static CustomManager sInstance;            public static CustomManager getInstance(Context context) {                if (sInstance == null) {                    sInstance = new CustomManager(context);                }                return sInstance;            }            private Context mContext;            private CustomManager(Context context) {                mContext = context;            }        }



这里的问题在于,我们不知道这个Context是从哪里来的,并且如果保存一个最终指向的是Activity或者Servece的引用是并不安全的. 这是一个问题,是因为一个单例在类的内部维持一个唯一的静态引用,这意味着我们的对象,以及所有其他它所引用的对象,将永远不能被垃圾回收. 假如这个Context是一个Activity,我们将保存与这个Activity相关的所有的view以及其他大的对象,从而造成内存泄漏.

为了解决这个问题,我们修改单例永远只是保存Application Context

public class CustomManager {    private static CustomManager sInstance;    public static CustomManager getInstance(Context context) {        if (sInstance == null) {            //Always pass in the Application Context            sInstance = new CustomManager(context.getApplicationContext());        }        return sInstance;    }    private Context mContext;    private CustomManager(Context context) {        mContext = context;    }}

现在这个例子中,我们的Context来自哪里都没有关系,因为我们这里保存引用是安全的. Application Context 本身就是一个单例,所以我们再创建另外一个static引用,不会造成任何内存泄漏. 另外一个很好的例子是,在后台线程或者一个等待的Handler中保存Context的引用,也可以使用这样的方法.

为什么我们不能总是引用Application Context呢?正如前面说的,引用Application Context永远不用担心内存泄漏的问题. 问题的答案,就像我在开始的介绍中说的,是因为不同context并不是等价的.


Context功能

Context能什么取决于该Context创建于哪里.

-ApplicationActivityServiceContentProviderBroadcastReceiver显示DialogNOYESNONONO启动ActivityNO1YESNO1NO1NO1Layout InflationNO2YESNO2NO2NO2启动ServiceYESYESYESYESYES绑定到ServiceYESYESYESYESNO发送BroadcastYESYESYESYESYES注册BroadcastReceiverYESYESYESYESNO3加载ResourceYESYESYESYESYES


  1. NO1 表示Application context的确可以开始一个Activity,但是它需要创建一个新的task. 这可能会满足一些特定的需求,但是在你的应用中会创建一个不标准的回退栈(back stack),这通常是不推荐的或者不是是好的实践. 

  2. NO2 表示这是非法的,但是这个填充(inflation)的确可以完成,但是是使用所运行的系统默认的主题(theme),而不是你app定义的主题. 

  3. NO3 在Android4.2以上,如果Receiver是null的话(这是用来获取一个sticky broadcast的当前 值的),这是允许的.

用户界面UI

从前面的表格中可以看到,application context有很多功能并不是合适去做,而这些功能都与UI相关. 实际上,只有Activity能够处理所有与UI相关的任务. 其他类别的Context实例功能都差不多.

幸运的是,在应用中这三种操作基本上都不需要在Activity范围之外进行,这很可能是android框架故意这么设计的. 尝试显示一个使用Aplication Context创建的Dialog,或者使用Application Context开始一个Activity,系统会抛出一个异常,让你的Application崩溃,非常强的告诉你某些地方出了问题.

一个并不明显的问题是填充布局(inflating layout). 如果你已经读过了我(原文作者)的上一篇文章Layout inflation,你就已经知道它可能是一个非常神秘过程,伴随一些隐藏的行为. 使用正确的context关系到其中的一个行为。当你使用Application context来inflate一个布局的时候,框架并不会报错,并返回一个使用系统默认的主题创建一个完美的view给你,而没有考虑你的Applicaiton自定义的theme和style. 这是因为Acitivity是唯一的绑定了在manifast文件种定义主题的Context. 其他的Context实例将会使用系统默认的主题来inflater你的view. 导致显示的结果并不是你所希望的.

规则

可能有些读者已经得出两个规则互相矛盾的结论. 可能有些情况下,在某些Application的设计中,我们可能既必须长期保存一个的引用,并且为了完成与UI相关的工作又必须保存一个Activity. 如果出现这种情况,我将会强烈建议你重新考虑你的设计,它将是一个很好的“反框架”教材.

经验

通常,使用组件内能够直接获取的Context. 只要该引用没有超过所在组件的生命周期,便可以安全的保存该引用. 一旦超过了Activity或者Service的生命周期范围,就应该将Context引用转换为为Application Context.



阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 便携式开孔机 木头开孔机 进口开孔机 瓷砖开孔机 地下开孔机 不锈钢圆管开孔机 家具开孔机 薄膜开孔机 微型开孔机 木材开孔机 护栏开孔机 液压开孔机价格 手持开孔机 电动管道开孔机 管道带水开孔机 铝型材开孔机 自来水管开孔机 槽钢开孔机 电动液压开孔机 木板开孔机 水泥开孔机 水钻开孔机价格 锁孔开孔机 自来水管道开孔机 石板开孔机 铁板开孔机 角铁开孔机 砖墙开孔机 海绵开孔机 混凝土开孔机价格 钢筋混凝土开孔机 油压开孔机 防盗窗开孔机 开孔机配件 铁皮开孔机 铰链开孔机 qq开孔机 各种钢轨开孔机 镀锌钢管开孔机 手动带压开孔机 便携式门锁开孔机