Android之Context底层原理
本篇文章已授权微信公众号code小生发布
1.Context基本概念
Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”。从Android系统的角度来理解:Context是一个场景,代表与操作系统的交互的一种过程。Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与 。从程序的角度上来理解:Context是个抽象类,而Activity、Service、Application等都是该类的一个实现。
2.Context与Activity、Service、Application关系
Context类本身是一个纯abstract类,他有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。
ContextThemeWrapper类,其内部包含了与主题(Theme)相关的接口,这里所说的主题就是指在AndroidMainifest.xml中通过android:theme为Application元素或者Activity元素指定的主题。当然,只有Activity才需要主题,Service是不需要主题的,Application同理。
而ContextImpl类则真正实现了Context中的所有函数,应用程序中所调用的各种Context类的方法,其实现均来于该类。Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。
3.Context作用域
Activity的作用域最广,Application和Service在启动Activity需要创建一个新的task,一般不推荐。layout infalte也是合法的,但是会使用系统默认的主题样式,如果自定义某些样式可能不会被使用
4.Context的使用
4.1Context的数量
一个应用程序中到底有多少个Context呢?根据Context的类型可以得出,一共有Application、Activity和Service三种类型,因此一个应用程序的Context数量为:
Context数量=Activity数量+Service数量+1
4.2如何获取Context
1)View.getContext,返回当前Activity所在的应用进程的Context对象,通常是当前正在展示的Activity对象
2)Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
3)Activity.this返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast因为是系统层级的Windows,直接使用ApplicationContext则可。
4)getApplication和getApplicationContext获取的对象时一致的。但是getApplication方法只有在Activity和Service中才能调到。但例如BroadcasrReceiver中需要获取Application,则需要借助getApplicationContext()方法。
4.3Context引起的内测泄漏
1)错误的单例模式
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context;
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉
2)View持有Activity引用
public class MainActivity extends Activity {
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
}
}
有一个静态的Drawable对象当ImageView设置这个Drawable时,ImageView保存了mDrawable的引用,而ImageView传入的this是MainActivity的mContext,因为被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,MainActivity被销毁时,也不能被GC掉,所以造成内存泄漏。
5.总结
- 尽量使用Application的Context
- 不要让生命周期长于Activity的对象持有其的引用
- 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类示例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有