在平常的开发过程中经常会遇到传入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自然就不会造成内存泄漏了。
网友评论