天天看点

Context你真的了解吗?前言

前言

Context是什么?

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被android系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用环境变量等)的上下文。

Context原理图!

Context你真的了解吗?前言

原理图分析:

Context是一个抽象类,Context有两个子类:ContextImpl和ContextWrapper,两个类的区别ContextImpl类是具体实现功能的类,而ContextWrapper只是用来进行一些包装等,但是会看到ContextThemeWrapper和Application还有Service都是继承了ContextWrapper,不是说ContextImpl是具体实现内容的类吗?因为那三个内容初始化过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法,看原理图中ContextWrapper指向ContextImpl的箭头就是这个意思。

Context的获取方式

  • View.getContext,返回当前View的Context对象,通常就是当前正在显示的Activity对象
  • Activity.this,返回当前Activity的实例,一般UI相关的使用这个作为Context对象(Toast有些特殊,可以直接使用ApplicationContext,因为Toast的Window是系统Window)
  • Activity.getApplicationContext()获取当前Activity所在应用进程的Context对象

    注意:这里还有个getApplication,getApplication和getApplicationContext有什么区别?

    getApplication和getApplicationContext获取的是同一个实例;getApplication方法的语义更强,但是只有在Activity或者Service中使用Application;但是如果在BroadcastReceiver等其它场景获取实例,只能借助getApplicationContext

  • ContextWrapper.getBaseContext()知道有这个获取方式就行

Context的使用场景

Context作用域 Application Activity Service
show a dialog No YES No
strat an Activity 不推荐 YES 不推荐
Layout Inflation 不推荐 YES 不推荐
Start a Service YES YES YES
Send a Broadcast YES YES YES
Register Broadcast Receiver YES YES YES
Load Resource Values YES YES YES

上面的表格时对整体Context使用场景的一个汇总,在这里只对前三条特殊的进行说明:

- show a dialog

Dialog时不允许凭空出现,必须有对应Activity,才能在Window上显示(有视图的地方就有),所以需要使用Activity的Context,Application和Service不能显示Dialog。但是在Activity中,如果非要使用Application的context来开启dialog一定不可以吗?不是的,需要改变dialog的一个属性,修改为在系统级别Window显示,具体请看博客:https://blog.csdn.net/xgangzai/article/details/81390630中的Dialog的Window创建过程

- start an Activity

启动一个Activity,用Application的Context会报错,这是因为Application的context并没有所谓的任务栈。新启动的Activity找不到任务栈无法入栈。但是这种办法也可以解决,为待启动的Activity指定一个flag标记位,这样启动的时候会为它创建一个新的任务栈,这种Activity是以singleTask方式启动。(不推荐)

- Layout Inflation

使用Application中的context或者service中的context,自定义的主题样式不会被使用。

总结:一般和UI相关的都使用Activity的context。

Context相关问题

面试中可能会问到一个应用程序中正常情况下会包含多少个Context?

Context的数量=Activity的数量+Service的数量+1

其中1指的是Application的数量

四大组件中BroadcastReceiver,ContentProvide并不是Context的子类,在使用的时候都是通过其它地方传递过去。

Activity为什么不能直接new出来?

在刚接到项目代码时候,代码中竟然出现了在变量声明的时候,直接new出来一个Activity,当时真的很无语。那么说一下Activity为什么不能直接new出一个对象呢?

Android程序和java程序不一样,java中创建一个类,写一个Main方法就可以跑了,Android应用时基于组件的应用设计模式,组件的运行还必须有一个完整的工程环境,环境我们在上面说了也就是Context,只有在这种上下文环境(Context)下,Activity,Service,BroadcastReceiver等系统组件才能正常工作,所以这些组件直接采用java普通的对象创建形式new出来时不可行的。

Context造成的内存泄漏?

错误的单例模式

传进来Activity的context之后一直保存Activity传递进来的context引用,即使Activity被销毁掉也不会回收context引用。造成内存泄漏,(简单的说,Activity的context往出传的时候多想想,尽量不要)

代码如下:

public class Singleton(){
    private static Single instance;
    private Context context;

    private Singleton(Context context){
        this.context=context;
    }

    public static Singleton getInstance(Context context){
        if(instance==null){
            instance=new Singleton(context.getApplication());
        }
        return instance;
    }
}
           

总结,如何正确使用Context

  • Application的Context能解决的情况下,优先使用Application的context
  • 不要让生命周期长于Activity的对象持有到Activity的引用,不然会造成内存泄漏
  • 静态内部类使用的时候要注意,进了不要再Activity中使用,因为非静态内部类会隐式持有外部实例的引用,如果使用静态内部类,将外部实例引用为弱引用持有(联想ListView使用的时候Adapter的创建)

android内存泄漏相关内没进行详细总结,可参考文章学习:

https://blog.csdn.net/u010198148/article/details/51649852

继续阅读