作为一个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使用的时候要注意使用方式,否则很可能造成内存泄漏
例子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中使用非静态的内部类。因为非静态的内部类会隐式持有外部类的引用。如果要使用静态内部类,使用弱引用来持有外部类的实例。
————————————————
版权声明:本文为CSDN博主「N0tExpectErr0r」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_21556263/article/details/83211897