安卓Context介绍

Context是什么

Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”,那么这个“上下文”到底是指什么意思呢?在语文中,我们可以理解为语境,在程序中,我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。比如微信聊天,此时的“环境”是指聊天的界面以及相关的数据请求与传输,Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。

那Context到底是什么呢?一个Activity就是一个Context,一个Service也是一个Context。Android程序员把“场景”抽象为Context类,他们认为用户和操作系统的每一次交互都是一个场景,比如打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,比如后台运行的服务(Service)。一个应用程序可以认为是一个工作环境,用户在这个环境中会切换到不同的场景,这就像一个前台秘书,她可能需要接待客人,可能要打印文件,还可能要接听客户电话,而这些就称之为不同的场景,前台秘书可以称之为一个应用程序。

Context继承关系

Activity,Service,Application都是一个Context,他的继承关系如下图
这里写图片描述

可以看到只有ContextImpl和ContextWrapper直接继承自Context,而查看ContextWrapper的源码会发现,它没有做任何实际上的工作,他的所有工作都是由其内部的一个

Context mBase;

mBase变量完成的,而其提供了attachBaseContext方法去为mBase赋值,我们以Activity为例,来看看mBase变量是什么

Activity的创建是在ActivityThread的performLaunchActivity方法中通过反射完成的,而Activity创建完毕后会去调用Activity的attach方法

Context appContext = createBaseContextForActivity(r, activity);
......
activity.attach(appContext, this, getInstrumentation(), r.token,
      r.ident, app, r.intent, r.activityInfo, title, r.parent,
      r.embeddedID, r.lastNonConfigurationInstances, config,
      r.referrer, r.voiceInteractor, window);

注意其中的appContext参数,来看他是如何创建的

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        int displayId = Display.DEFAULT_DISPLAY;
        try {
            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if (pkgName != null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if (id != Display.DEFAULT_DISPLAY) {
                    Display display =
                            dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
                    baseContext = appContext.createDisplayContext(display);
                    break;
                }
            }
        }
        return baseContext;
    }

可以看到在createBaseContextForActivity方法中返回的正是ContextImpl

在回到Activity的attach方法中

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);
        ......
    }

通过ContextWrapper的attachBaseContext方法,将真正起作用的对象ContextImpl传递并保存了起来

我们在Activity通常调用的比如startActivity,getSystemService等方法最终都会到ContextImpl的方法中

@Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

@Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

在上面的继承关系中我们还看到Activity并不是直接继承自ContextWrapper,中间还有一个ContextThemeWrapper,从名字也可以看出来,他是专门为了处理主题,也就是我们平时为Activity设计的风格,因为在Activity,Service和Application中,只有Activity需要显示界面,所以为他多设计了一层,下面是ContextThemeWrapper相对于ContextWrapper多出的方法

    public void setTheme(int resid) {
        ......
    }

    public int getThemeResId() {
        ......
    }

    @Override
    public Resources.Theme getTheme() {
        ......
    }

    @Override
    public Object getSystemService(String name) {
        ......
    }

    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
        ......
    }

    private void initializeTheme() {
        ......
    }

    @Override
    public AssetManager getAssets() {
        ......
    }

Context引起的内存泄露

Context并不能随便乱用,用的不好有可能会引起内存泄露的问题

比如说在单例模式中引用了Context

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;
    }
}

如果传入的context为Activity,那么在Activity生命周期结束时,由于静态对象instance会持有context对象,则会导致Activity无法被回收造成内存泄露

所以在使用Context时应注意以下几点:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值