美文网首页
让人迷惑的context

让人迷惑的context

作者: 李die喋 | 来源:发表于2019-09-27 20:33 被阅读0次

    在平常的开发过程中经常会遇到传入context的问题,之前简单的查过一些博客,现在好好的总结一下。

    Context是什么

    Context可以理解为上下文 环境变量。它

    • 描述应用程序环境的信息,即上下文;
    • 是一个抽象类,有具体类去实现;
    • 通过它可以获取应用程序的资源和类,也包括一些应用级别操作,例如启动一个Activity、发送广播、接收Intent等。

    下面是它的一个简易的类继承图:


    context

    可以看到,ContextImpl和ContextWrapper继承Context,Application、service、Acitvity都是Context的子类。所以我们在Activity中传入参数的时候经常会传入一个this,有的时候也会传入一个xxxActivity.this。传入xxxActivity.this的时候出现在匿名内部类中,因为this代表的是匿名类对象。

    ContextImpl真正实现了Context类,Activity和Service没有真正的实现,他们的内部只是包含了一个真实的Context对象而已。也就是在创建Activity和Service的时候会创建一个ContextImpl对象,并赋值到Activity的Context类型变量中。

    据统计在Android源码中有七处创建了ContextImpl:

    • PackageInfo.makeApplication()
    • performLaunchActivity()中
    • handleCreateBackupAgent()中
    • handleCreateService()中
    • 2次在handleBinderApplication()中
    • 在attach()方法中

    这些会在后面的文章进行分析。

    getApplicationContext()和xxxAcitvity.this

    之前不清楚为什么在传入context的地方也可以传入activity,现在经过看源码中的继承关系activity是context的子类,因此可以那样传入数据。可以认为在activity中activity和context是一样的。

    xxxAcitvity.this指的是当前的Activity对象,getApplicationContext()返回一个Context对象,是当前Application的一个实例。可以理解为他们存在的周期不同,activity对应的context存在于活动的生命周期,所以在两个activity之间的跳转传入的是当前的activity;弹出一个dialog的时候传入的也是当前的activity,它依附于activity而存在。其他的情况,比如数据库的调用等情况用getApplication获取全局的context就可以。

    //ContextWrapper中 调用了父类的方法
    //Context mBase;
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    
    //ContextImpl中的方法
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    

    可以看到ContextImpl中的getApplicationContext返回的是Application对象,只不过返回类型为Context。

    对于第三方应用来说,一个应用只存在一个Application对象,且通过getApplication和getApplicationContext得到的是同一个对象,两者的区别仅仅是返回类型不同。

    一个易于混淆的点

    单例模式在日常的开发中是会频繁使用的,我之前都是这样写的:

    public class Utils{
        private static Utils INSTANCE;
        private Context mContext;
        
        public Utils(Context context){
            this.mContext = context;
        }
        
        public static Utils getInstance(Context context){
            if(INSTANCE == null){
                synchronized(Utils.class){
                    if(INSTANCE == null){
                        INSTANCE = new Utils(context);
                    }
                }
            }
        }
    }
    
    //在使用的时候会传入一个this
    Utils utils = Utils.getInstance(this);
    

    这样的写法会造成内存泄漏。因为这里传入的是Activity的context,这个context在这个地方是个强引用,被静态变量所持有。static变量的生命周期伴随着进程的诞生和销毁,也就是说静态变量伴随了app进程的整个生命周期,由于INSTANCE持有了context,在acticity退出的时候,context没有办法得到释放,就造成了内存泄漏。

    解决方法就是this.mContext = context.getApplicationContext()。Application伴随了应用的始终,所以使用Application的context自然就不会造成内存泄漏了。

    相关文章

      网友评论

          本文标题:让人迷惑的context

          本文链接:https://www.haomeiwen.com/subject/nslfuctx.html