26
2017
09

Android 浅析Context

我们平时在开发Android应用程序时一直都在使用Context比如启动一个Activity,大部分人不知道为什么要使用context来启动Activity,不能直接new一个activity而要使用context来启动,一个App到底有多少个Context等等的细节,尤其是Context使用不当还会造成内存泄漏,所以有必要写篇文章总结下。

Android应用模型

磨刀不误砍柴工,先介绍一下Android的应用模型,Android应用是用java写的,那么他和java程序有什么区别呢?其实Android不像java一样随便写个类,有个main方法,就能运行,它是基于组件的应用模式,这些组件包括activity、service等,一个个组件协同合作,而context是他们协同合作的基础,它们不能通过new来产生一个组件,而是要有它们各自的上下文环境,也就是我们这里讨论的Context。可以这样讲,Context是维持Android程序中各组件能够正常工作的一个核心功能类。想象一下,Android应用就像一个公司,各个组件就是各个职员,而context就是公司环境,职员需要在这个环境中进行工作,即便新的职员来公司了也不能不在这个环境中工作(即不能new一个)。

理解Context

Context:上下文环境,其实这是一个很抽象的解释,需要具体代入一些生活场景,初学者才会理解,就比如聊天的时候,对面发了一堆消息来了,而你要回他的话,肯定要考虑他发的话(上文环境),再考虑自己怎么回答(下文环境),这就是聊天环境(上下文环境),Android程序员把“场景”抽象为Context类,他们认为用户和操作系统的每一次交互都是一个场景,比如打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,比如后台运行的服务(Service)。

Context的继承结构分析

context继承结构图
Context的继承结构还是稍微有点复杂的,可以看到,直系子类有两个,一个是ContextWrapper,一个是ContextImpl。那么从名字上就可以看出,ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类。而ContextWrapper又有三个直接的子类,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity。

那么在这里我们至少看到了几个所比较熟悉的面孔,Activity、Service、还有Application。由此,其实我们就已经可以得出结论了,Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的。
至此我们就可以得出一个结论一个应用中的context数量应该是Activity、Service、还有Application数量之和。

Context使用建议

但Context并不能随便乱用,用的不好有可能会引起内存泄露的问题,下面就示例两种错误的引用方式。

错误的单例模式

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,假如Activity A去getInstance获得instance对象,传入this,常驻内存的Singleton保存了你传入的Activity A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。
View持有Activity引用

public class MainActivity extends Activity {
    private static Drawable mDrawable;

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

有一个静态的Drawable对象当ImageView设置这个Drawable时,ImageView保存了mDrawable的引用,而ImageView传入的this是MainActivity的mContext,因为被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,MainActivity被销毁时,也不能被GC掉,所以造成内存泄漏。

正确使用Context

一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

本文旨在浅析Context,也算是自己学习的一个总结,如要深入研究可移步
http://blog.csdn.net/guolin_blog/article/details/47028975
参考内容:
http://www.jianshu.com/p/94e0f9ab3f1d

上一篇:聊聊SQLite - 基础篇 下一篇:Java中CyclicBarrier的用法和示例