Context详解

翻译 2016年05月30日 17:15:52

原文

为了更好的使用Context,翻译了一篇介绍Context的文章,原文链接:Context, What Context?


什么是Context

Context可能是Android应用中最经常使用的对象成员,但是也可能是最被错误使用的对象成员.

Context对象在Android应用中太常见了,而且在应用中经常作为参数传来传去,甚至是不经验间就创造出一个调用者并不想创造的上下文环境出来.Context的应用范围极广,例如:加载资源,启动一个Activity,获取系统服务,获取文件路径或者创建一个View.
接下来,会讲解一下如何在你的应用中更高效的使用Context对象.


Context Types

Context分为几种类型,而且这几种类型并不是等价的.Context类型如下:

  • Application: Application在应用程序中是单例运行.可以在Activity或者Service中通过getApplication()方法获取,或者在任何继承Context的类中通过getApplicationContext()方法获取.不过无论是通过哪种方法获取Application的实例,Application的实例都是唯一的,相同的.
  • Activity/Service: Activity/Service都继承自ContextWrapper,因为具有相同的Context API.每当Framework创建一个新的Activity或者Service实例的时候,同时会创建一个新的ContextImpl对象,ContextImpl就是最终处理所有Context API方法的内部对象.不同的Activity或者Service的Context都是不一样的.
  • BroadcastReceiver: BroadcastReceiver本身不是Context,但是Framework在每次发布它监听的broadcast event事件的时候,会给它的onReceive函数中传递了一个Context对象.这个Context对象是ReceiverRestrictedContext对象的实例,但是有两个方法是无法使用的:registerReceiver()和bindService().每次BroadcastReceiver通过onReceive()函数收到一个broadcast evnent时,传递过来的Context对象都是一个新的实例.
  • ContentProvider: ContentProvider本身不是Context,但是内部可以通过getContext()方法获取Context对象.如果ContentProvider的调用者和ContentProvider处于同一进程中,通过getContext()获取的就是当前进程唯一的Application Context.如果不在同一进程中,通过getContext()方法就会创建一个ContentProvider所在包的一个Context实例.

保存Context对象

第一个问题: 当我们将一个Context的引用保存到一个存活时间比Context本身生命周期还长的对象时,问题就来了.例如,创建一个自定义的单例对象,在单例对象中要求使用Context来加载资源或者获取ContentProvider,并且传入的Context对象是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对象,我们并不知道它是什么的实例.如果Context是Activity或者Service的实例,那这种写法是非常不安全的.之所以不安全,是因为:
CustomManager是一个单例类,并且被一个静态对象持有,那CustomManager的生命周期是跟类相关的,不会被回收.如果保存的Context对象是Activity/Service,那Activity/Service始终被强引用,哪怕生命周期已经结束也没法被回收,这样导致了内存泄露.

正确单例

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;
    }
}

在上面的例子中,由于我们使用保存的是Application Context,这样我们就不需要关心Context是谁的实例了.因为Application生命周期是跟当前应用进程一致的,也就不存在内存泄露的风险了.这个处理技巧在后台线程或者Handler处理中同样有效.

但是为什么我们不能一直使用Application Context呢?这样我们永远不用担心因为Application导致的内存泄露了?
之所以不能一直用Application Context,是因为每个Context实例都不是相同的,它们的功能也是不同的,例如Application Context就没法startActivity.


Context 功能列表

下面的表格描述了5中Context实例的功能(非常重要):

行为 Application Activity Service ContentProvider BroadcastReceiver
Show a Dialog NO YES NO NO NO
Start an Activity NO YES NO NO NO
Layout Inflation NO YES NO NO NO
Start a Service YES YES YES YES YES
Bind to a Service YES YES YES YES NO
Send a Broadcast YES YES YES YES YES
Register BroadcastReceiver YES YES YES YES NO
Load Resource Values YES YES YES YES YES

用户界面

读者可以从上面的表格中看出Application还是有很多不适合场景的.这些不适合的场景基本都和用户界面相关.实际上,也只有Activity的Context能够处理UI相关例子,其它Context的功能基本相同.

幸运的是,启动一个Dialog,启动一个Activity,实例化一个LayoutInflation这三个行为只有Activity能够操作,其他的Context类型都不能操作.Framework这么设计,也是为了最大限度的避免Context误用.


规则冲突

一定要人会说上面的规则存在了矛盾,例如:应用中需要一个长期存在的Context,并且这个Context是需要能操纵UI的,因此只能长期保存Activity句柄.

本文原创作者对这个矛盾的回应是:请重新设计你们的需要,代码和架构


Context使用技巧

大多数情况下,在组件的生命周期之内可以直接使用组件自身的Context,一旦需要在超出组件生命周期之外的对象中使用Context,就应该只用Application context,哪怕只是临时的引用,也是如此.

相关文章推荐

contextTypes was defined as an instance property on MyButton. Use a static property to define contex

问题描述:在使用es6语法时,定义一个类方法时,出现如下的问题: contextTypes was defined as an instance property on MyButton. Use ...
  • suwu150
  • suwu150
  • 2017年02月17日 19:48
  • 739

理解Context内涵

原题目:Context都没弄明白,还怎么做Android开发? 文/尹star(简书作者) 转自:http://www.jianshu.com/p/94e0f9ab3f1dActivity mAc...

Android Context 上下文 你必须知道的一切

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40481055,本文出自:【张鸿洋的博客】本文大多数内容翻译自:http://ww...

android中this、Activity.this、context区别与联系

参考博文:http://www.cnblogs.com/wenjiang/archive/2012/10/15/2724923.html private Context mContext; mCo...

Android Context 上下文 你必须知道的一切

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40481055 本文大多数内容翻译自:http://www.doubleenco...

Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)

Linux进程上下文切换过程context_switch详解 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.6 X86 &...
  • gatieme
  • gatieme
  • 2016年07月10日 18:25
  • 9230

Android中的context、activity、intent、service使用详解

在一个Android应用中,主要是由四种组件组成的,这四种组件分别是Context,Activity,Intent,Service。 Content被译为上下文,是应用程序中心,应用程序所有功能...

Android中的Context详解

Android基础概念Context的作用 Context字面意思上下文,位于framework package的android.content.Context中,其实该类为LONG型,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Context详解
举报原因:
原因补充:

(最多只允许输入30个字)