Context

一、概念

Context是运行上下文环境,具有以下功能:
获取应用资源,如drawable、string、assets;
获取文件目录,如获取/data/分区的数据目录、获取sdcard目录;
获取系统服务,如AMS、WMS、PMS;
操作四大组件,如启动界面、发送广播、绑定服务、打开数据库;
检查授予权限,如应用向外提供服务时,可以判定申请者是否具备访问权限。

二、类图

Context 类图

Context类是一个抽象类,具体实现在ContextImpl类中。

ContextWrapper是Context的一个包装类,其里面所有的方法实现都是调用其内部mBase变量的方法,而mBase就是ContextImpl对象。ContextWrapper有一个ContextThemeWrapper子类,该类扩展了主题相关的方法。

Application和Service继承自ContextWrapper,而Activity继承自ContextThemeWrapper,这是因为Activity在启动的时候系统都会加载一个主题,也就是我们平时在AndroidManifest.xml文件里面写的android:theme=”@style/AppTheme”属性,而Applicaton和Service都和UI界面没有关系,因此它们继承自ContextWrapper。

Application扩展了应用程序的生命周期,Activity扩展了界面显示的生命周期,Service扩展了后台服务的生命周期,它们在父类Context的基础上进行了不同维度的扩展。虽然BroadcastReceiver和ContentProvider不是Context的子类,但是BroadcastReceiver的onReceive()方法和ContentProvider的构造方法都需要把Context作为参数传入,虽然它们不继承于Context,但是它们都依赖于Context,换个角度看它们也是装饰器,包装了Context。

三、Context类型

Application Context

Application Context在应用进程里是一个单例,可以在Activity或Service中通过getApplication()来访问,也可以在任意继承Context的对象中通过getApplicationContext()来访问。不管以何种形式访问, 在同一应用进程中获得的Application Context实例都是同一个。

Activity/Service Context

Activity/Service继承自ContextThemeWrapper/ContextWrapper,ContextWrapper作为Context(一个Base Context)的装饰器,实现了和Context一样的接口。当创建一个Activity/Service实例时,也会创建一个ContextImpl的实例来真正处理Context接口所描述的工作,每个Activity/Service实例都持有一个对应的Base Context的实例。

BroadcastReceiver中的Context

BroadcastReceiver不是一个Context,但广播事件到来时会传递一个Context给onReceive()方法。
当广播接收器在manifest中静态注册时,传递的是ReceiverRestrictedContext实例,该实例全局只有一个,它禁用了Context的registerReceiver()方法和bindService()方法。
当广播接收器在activity/service中动态注册时,传递的是activity/service实例(向上转型为Context类型),没有禁用Context的方法。

ContentProvider中的Context

ContentProvider不是一个Context,但是可以通过getContext()方法来获取一个Context实例,该实例属于Application级别。

四、应用场景

Context 应用场景

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

对于应用场景,实际上只要把握住一点:凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,使用Activity、Service、Application等实例都可以,但是要特别注意Context引用的持有,防止内存泄漏。

五、注意事项

不同类型Context的区别

// 在Activity.onCreate()中插入以下代码:
Log.i(TAG, "Application: " + getApplication());
Log.i(TAG, "Application Context: " + getApplicationContext());
Log.i(TAG, "Activity: " + this);
Log.i(TAG, "Activity Context:" + this);
Log.i(TAG, "Application BaseContext: " + getApplication().getBaseContext());
Log.i(TAG, "Activity BaseContext: " + getBaseContext());

// 得到的运行结果:
I MainActivity: Application: com.duanqz.github.DemoApp@cf8644e
I MainActivity: ApplicationContext: com.duanqz.github.DemoApp@cf8644e
I MainActivity: Activity: com.duanqz.github.MainActivity@bbcadec
I MainActivity: Activity Context: com.duanqz.github.MainActivity@bbcadec
I MainActivity: Application BaseContext: android.app.ContextImpl@6a6a96f
I MainActivity: Activity BaseContext: android.app.ContextImpl@770267
  1. getApplication()和getApplicationContext()返回的是同一个对象,但对象的类型不同,前者是Application,后者是Context。Java是强类型的语言,从Application到Context相当于向上转型,会丢失掉一些接口的访问入口。
  2. Activity和Activity Context也是同一个对象,不同的类型。
  3. Application和Activity的Base Context都是ContextImpl对象,正是这个Context真正的实现类,被外围的修饰器包装了一下,才形成不同功能的类。

Context导致的内存泄露问题

public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
       ... // Other codes
       Singleton.get(this);
    }
}

public class Singleton {
    private static Singleton sMe;
    private Singleton(Context context) {
        // Do something with context
    }

    public static synchronized Singleton get(Context context) {
        if (sMe == null) {
            sMe = new Singleton(context);
        }
        return sMe;
    }
}


//避免内存泄露方法:
public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
       ... // Other codes
       Singleton.get(getApplicationContext());
    }
}

单例的实现都包含一个静态变量,在Java的垃圾回收机制中,静态变量是GC ROOT,某对象只要存在到达GC ROOT的路径,就不会被回收。如果对象的生命周期已经结束但是不能被回收,则会出现内存泄露问题。

使用Application Context的场景:
Context的生命周期超出Activity或Service的生命周期时,如工具类。

使用Activity Context的场景:
Context的生命周期小于Activity时,如初始化Activity的子控件、弹出对话框。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容