关于Context的理解与总结——什么是Context?

作为一个Android开发者,我们在Android开发中经常会使用到Context这个类。它在加载资源、启动Activity、获取系统服务、创建View等活动中都需要参与。

但Context到底是什么,我就很少去关注了…那么我们该如何理解去Context呢?它到底是什么呢?

什么是Context

翻译角度

Context翻译为中文,有:上下文、背景、环境等翻译,我们可以把Context理解成一种环境。Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境 。

因此Android不像普通Java程序一样,随便创建一个类,写上main方法就可以运行,每个组件需要有自己工作的环境,才能正常运行。而Context,就是我们这里所说的环境。

比如,当我们需要创建一个Button时,也需要给它提供一个环境:Button button = new Button(context);

一个Activity可以是一个Context,一个Service也可以是一个Context。

源码角度

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {
...
}

我们可以看到Context源码中的注释,里面说到Context提供了关于应用环境的全局信息的接口,它是抽象类,调用由Android系统来进行。它可以获取有应用特征的资源和类,可以执行一些应用级别的操作(如启动Activity,发送广播,接收Intent等等)

Context是一个抽象类,它有两个实现的子类——ContextImpl 和 ContextWrapper。

ContextWrapper类仅仅是一个包装类,它的构造函数需要传递一个真正的Context的引用。并且它提供了attachBaseContext() 方法来指定真正的Context。调用ContextWrapper最终都会调用它包含的真正的Context对象的方法。

ContextImpl才是真正的实现类,它实现了Context中的所有方法。平时我们调用的所有Context的方法的实现均来自这个类。

Activity、Application、Service三个类均继承自ContextWrapper,而在具体初始化过程中,则会构造ContextImpl对象,来实现Context中的方法。

Context的作用域

Context在我们日常开发中使用的非常广泛,但是我们并不是拿到了Context就可以为所欲为。Context的使用会有一些规则的限制。具体的限制方式我们可以参考下面这张表:

img

如何获取Context

要获取一个Context,有下面的四种方法:

  • **View.getContext():**返回当前View对象的Context对象,通常是正在展示的Activity对象。
  • **Activity.getApplicationContext():**获取当前Activity所在的Application的Context对象。(通常我们使用Context对象时,要优先考虑这个全局的进程Context)
  • **ContextWrapper.getBaseContext()**要获取一个ContextWrapper装饰前的Context,可以使用这个方法。
  • **Activity.this():**返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象。但是Toast实际上使用ApplicationContext也可以。

Context引起的内存泄漏

Context使用的时候要注意使用方式,否则很可能造成内存泄漏

例子1

比如下面这种错误的单例:

public class Singleton {
    private static Singleton instance;
    private Context mContext;

    private Singleton(Context context) {
        this.mContext = context;
    }

    public static Singleton getInstance(Context context) {
        if (instance == null) {
            instance = new Singleton(context);
        }
        return instance;
    }
}

上面是一种线程不安全的单例,instance是它的静态对象,生命周期比普通对象长。假如我们使用Activity去调用getInstance获取instance,则Singleton类保存了Activity的引用,导致Activity被销毁后仍然不能被GC回收。

例子2

public class MainActivity extends Activity {
    private static Drawable mDrawable;

    @Override
    protected void onCreate(Bundle saveInstanceState) {
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        ImageView ivImage = new ImageView(this);
        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        ivImage.setImageDrawable(mDrawable);
    }
}

在上面这个例子中,Drawable是静态的。调用ImageView的setImageDrawable设置Drawable时,ImageView就会持有这个Drawable的引用。而ImageView同时还持有Activity的引用,并且它持有的Drawable是常驻内存的,导致MainActivity被销毁时,无法被GC回收。

如何避免

一般Context所导致的内存泄漏,都是由于Context被销毁时,由于它的引用导致无法被回收。但是我们可以使用Application的引用,因为Application的生命周期是随着进程存在的。

因此我们尽量在使用Context的时候用如下的姿势:

  • 生命周期长的对象,并且Application的Context可以满足使用时,优先使用Application的Context。
  • 不要让声明周期比Activity长的对象持有Activity的引用
  • 尽量不要在Activity中使用非静态的内部类。因为非静态的内部类会隐式持有外部类的引用。如果要使用静态内部类,使用弱引用来持有外部类的实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值