Android Context
Android的Context功能
1.获取Resiurces,从而得到drawable,color,string 等
2.启动指定的组件
3.获取系统服务
4.获取目录
错误使用Context导致内存泄漏
错误使用Context可能会导致内存泄漏,经典的例子就是在实现单例模式是使用Context,如下代码是可能会导致内存泄漏的
1 | public class SingleInstance { |
如果使用者调用getInstance时传入的Context是一个Activity或者Service的实例,那么在应用退出之前,由于单例一直存在,会导致对应的Activity或者Service被单例引用,从而不会被垃圾回收,Activity或者Service中关联的其他View或者数据结构对象也不会被释放,从而导致内存泄漏,正确的做法使用Appliction Context,因为他是应用唯一的,而且生命周期是跟应用一致的,正确的使用单例的实现如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class SingleInstance {
private Context mContext;
private static SingleInstance sInstance;
public SingleInstance(Context context) {
mContext = context;
}
public static SingleInstance getsInstance(Context context){
if (sInstance == null){
sInstance = new SingleInstance(context.getApplicationConext());
}
return sInstance;
}
}
Context的种类
根据Context依托的组件以及用途不同,我们可以将Context分为如下几种。
- Application:Android应用中的默认单例类,在Activity或者Service中通过getAppliction()获取到我们的Context实例,通过context.getApplication()可以获取对应全局的唯一的Context实例
- Activity/Service:这个两个类都是ContextWrapper的子类,在这两个类中可以通过getBaseContext()获得到它们的Context实例,不同的Activity或者Service实例,它们的Context都是独立的,不能复用
- BroadcastReceiver:和Activity以及Service不同,BroadcatReceiver并不是Context子类,而是在回调函数onReceive()中由Android框架转入一个Context实例。系统传入的这个Context的这个Context实例是经过剪裁的,它不能调用registerReciver()以及bindSerivce()这两个函数
- ContentProvider: 同样的,ContextProvider也不是Context的子类,但是在创建是系统回传一个Context实例,这样在ContextProvider中可以通过getContext()函数获取。如果ContextProvider和调用者处于相同的应用进程中,那么getContext()将返回用工全局唯一的Context实例,如果是其他进程调用的ContextProvider,那么ContextProvider将持有自身所在的进程Context实例。
不同的Context的对比
不同组件中的Context能提供的功能不尽相同,总结如下
功能 | Application | Activity | Service | BroadcastReciver | ContentProivder |
---|---|---|---|---|---|
显示Dialog | NO | YES | NO | NO | NO |
启动Activity | NO[1] | YES | NO[1] | NO[1] | NO[1] |
实现Layout Inflation | NO[2] | YES | NO[2] | NO[2] | NO[2] |
启动Service | YES | YES | YES | YES | YES |
绑定Service | YES | YES | YES | YES | NO |
发送Broadcast | YES | YES | YES | YES | YES |
注册 | YES | YES | YES | YES | NO[3] |
加载资源Resource | YES | YES | YES | YES | YES |
NO[1]:对应的组件并不是真的不可以启动Activity,而是建议不要这么做,因为这些组件会在新的Task中创建Activity,而不是在原来的Task中。
NO[2]:也表示不建议这么做,在非Activity中进行LayoutInflation,会使用系统默认的主题,而不是应用中设置的主题
NO[3]:在Android 4.2和以上的系统上,如果注册的BroadcastRecvicer是NULL是可以的,用来获取skicky广播的当前值
获得Context方式
- 在Activity中使用getContext()
- 在Fragment使用在onAttch(Context context)方法中将Context设置为全局
- 在View和ViewGroup也有getContext(),获得Conext是View或者ViewGroup所在的Activity