一、Context的获取
获取context的方法有以下几种:
-
活动类中使用this关键字、getApplicationContext()、getBaseContext()
Activity.this 返回当前的活动类实例。
使用getApplicationContext获取整个应用的context,获取的对象存活周期和应用一样长。
getBaseContext 获取ContextWrapper的原始context,也就是获取到一个ContextImpl对象,这个方法在实际开发中使用并不多,也不建议使用。
-
在fragment类中,可以getActivity、getContext()
getContext: View中获取的是当前活动的activity,Fragment中返回与之关联的context
getActivity返回与当前fragment相关联的activity。
在view类中,可以用getContext(),返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
二、getApplicationContext()和getApplication()
这两个方法得到的结果都是相同的,原因在于,Application本身就是一个Context。但是这两个方法在作用域上存在比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了。getApplicationContext()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。
除此之外,还有getBaseContext方法,是得到一个ContextImpl对象。
ContextImpl是上下文功能的实现类。像Application、Activity这样的类其实并不会去具体实现Context的功能,而仅仅是做了一层接口封装而已,Context的具体功能都是由ContextImpl类去完成的。
三、方法执行顺序问题
为什么在Application类的构造方法中调用Context的方法就会崩溃,在onCreate()方法中调用Context的方法就一切正常?
这实际上是因为Application的方法的执行顺序是,先执行构造函数,然后执行attachBaseContext(),最后执行onCreate(),attachBaseContext()方法会将传入的一个Context参数赋值给mBase对象,之后mBase对象就有值了。而我们又知道,所有Context的方法都是调用这个mBase对象的同名方法,那么也就是说如果在mBase对象还没赋值的情况下就去调用Context中的任何一个方法时,就会出现空指针异常。所以在构造函数中调用Context的方法时,attachBaseContext()方法还没有执行,所以mBase对象为空,会崩溃。但是如果在onCreate方法中调用,mBase不为空,此时一切正常。
四、Context个数问题
APP Context总数 = Application数(1) + Activity数 + Service数
五、Context引起的内存泄露
Context不能随便乱用,用的不好可能会出现内存泄露问题,下面给出两种错误的引用方式的示例。
错误的单例模式
单例是我们比较简单常用的一种设计模式,然而如果单例使用不当也会导致内存泄露。例如:一个静态对象,持有一个context作为成员变量,其生命周期要长于普通的对象,当我们进行屏幕旋转,默认情况下,系统会销毁当前的Activity,但是当前的Activity被一个单例持有,导致垃圾回收器无法进行回收,进而产生了内存泄露。解决的方法就是不持有Activity的引用,而是持有Application的引用,由于ApplicationContext会存在于整个app的生命周期中,所以长期持有不会导致内存泄露。
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对象可以理解为随着进程存在的,所以总结如下:
1:当Application的Context能使用的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。