Android Context相关问题

一、Context的获取

获取context的方法有以下几种:

  1. 活动类中使用this关键字、getApplicationContext()、getBaseContext()

    • Activity.this 返回当前的活动类实例。

    • 使用getApplicationContext获取整个应用的context,获取的对象存活周期和应用一样长。

    • getBaseContext 获取ContextWrapper的原始context,也就是获取到一个ContextImpl对象,这个方法在实际开发中使用并不多,也不建议使用。

  2. 在fragment类中,可以getActivity、getContext()

    getContext: View中获取的是当前活动的activity,Fragment中返回与之关联的context

    getActivity返回与当前fragment相关联的activity。

  3. 在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中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

参考资料:https://www.jianshu.com/p/e3aa7265289a
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容