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