Context应该是每个入门Android开发的程序员第一个接触到的概念,它代表当前的上下文环境,可以用来实现很多功能的调用,语句如下。



//获取资源管理器对象,进而可以访问到例如 string, color 等资源
Resources resources = context.getResources();

//启动指定的Activity
context.startActivity(new Intent(this, MainActivity.class));

//获取各种系统服务
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

//获取系统文件目录
File internalDir = context.getCacheDir();
File externalDir = context.getExternalCacheDir();

//更多...



可见,正确理解Context的概念是很重要的,虽然应用开发中随处可见Context的使用,但并不是所有的Context实例都具备相同的功能,在使用上需要区别对待,否则和可能会引入问题。

Context的种类

根据Context依托的组件以及用户不同,我们可以将Context分为如下几类:

  • Application:Android应用中的默认单例类,在Activity或者Service中通过getApplication()可以获取到这个单例,通过context.getApplicationContext()可以获取到应用全局唯一的Context实例。
  • Activity/Service:这两个类都是ContextWrapper的子类,在这两个类中可以通过getBaseContext()获取到他们的Context实例,不同的Activity或者Service实例,它们的Context都是独立的,不会复用。
  • BroadcastReceiver:和Activity以及Service不同,BroadcastReceiver本身并不是Context的子类,而是在回调函数onReceive()中由Android框架传入一个Context的实例。系统传入的这个Context实例是经过功能裁剪的,它不能调用registerReceiver()以及bindService()这两个函数。
  • ContentProvider:同样的,ContentProvider也不是Context的子类,但在创建时系统会传入一个Context的实例,这样在ContentProvider中可以通过调用getContext()函数获取。如果ContentProvider和调用者处于相同的应用进程中,那么getContext()将返回应用全局唯一的Context实例。如果是其他进程调用的ContentProvider,那么ContentProvider将持有自身所在进程的Context实例。

 错误使用Context导致的内存泄漏

错误地使用Context可能会导致内存泄漏,典型的例子是在实现单例模式时使用Context,如下代码是可能会导致内存泄漏的单例实现。



public class SingleInstance{
	private Context mContext;
	private static SingleInstance sInstance;
	private SingleInstance(Context context){
		mContext = context;
	}
	public static SingleInstace getInstance(Context context){
		if(sInstance == null){
			sInstance = new SingleInstance(context);
		}
		return sInstance;
	}
}



如果使用者调用getInstance时传入的Context是一个Activity或者Service的实例,那么在应用退出之前,由于单例一直存在,会导致对应的Activity或者Service中关联的其他View或者数据结构对象也不会被释放,从而导致内存泄漏。正确的做法是使用Application Context,因为它是应用唯一的,而且生命周期是跟应用一致的,正确的单例实现如下。



public class SingleInstance{
	private Context mContext;
	private static SingleInstance sInstance;
	private SingleInstance(Context context){
		mContext = context;
	}
	public static SingleInstace getInstance(Context context){
		if(sInstance == null){
			sInstance = new SingleInstance(context.getApplicationContext());
		}
		return sInstance;
	}
}