阅读 267

Android之Context底层原理

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.1 Context的数量

一个应用程序中到底有多少个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.3 Context引起的内测泄漏

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中使用非静态内部类,因为非静态内部类会隐式持有外部类示例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有

关于

欢迎关注我的个人公众号

微信搜索:一码一浮生,或者搜索公众号ID:life2code

image