Android中Context认识

基于SDK29

1、Context作用

​ Context,意为上下文,阅读理解中常常有联系上下文理解的说法,这里可以认为Context是一个特定的范围,提供了整个环境的一些数据,比如Application作为Context可以注册Activity生命周期监听、获取应用进程名、获取应用资源等等,Activity作为Context可以获取资源、启动Activity、加载View等等, 具体使用见下图:

​ (以上图片来自博客:Android Context 上下文 你必须知道的一切)

2、Context分析

2.1、Context继承树

​ Context继承结构如下所示:

java.lang.Object:
  android.content.Context:
    android.app.ContextImpl  (Activity和Application中mBase具体操作实现类)
  
    android.content.ContextWrapper:
      android.app.Application
      android.app.Service

      android.view.ContextThemeWrapper:
        android.app.Activity:
          androidx.core.app.ComponentActivity:
            androidx.activity.ComponentActivity:
              androidx.fragment.app.FragmentActivity:
                androidx.appcompat.app.AppCompatActivity

​ 四大组件中Activity继承自ContextThemeWrapper,Service和Application继承自ContextWrapper。

2.2、ContextImpl类

​ ContextImpl类直接继承自Context类,位于android.app包下,提供了Context抽象方法的直接实现, 包括getAssets、getResources、getPackageManager等。

2.3、ContextWrapper分析

​ ContextWrapper使用静态代理的模式来管理Context,内部所有的操作都是通过Context类型的mBase来具体实现,如getResources()等。通过构造方法或者attachBaseContext方法对mBase赋值。

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }
    
    ...
}

2.4、ContextThemeWrapper分析

​ ContextThemeWrapper与ContextWrapper主要区别在于:前者能够直接处理主题,根据主题样式的id(mThemeResource)生成对应的主题Theme(mTheme),而后者需要由内部的mBase进行处理。

public class ContextThemeWrapper extends ContextWrapper {
    private int mThemeResource;
    private Resources.Theme mTheme;

    public ContextThemeWrapper() {
        super(null);
    }

    public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
        super(base);
        mThemeResource = themeResId;
    }

    public ContextThemeWrapper(Context base, Resources.Theme theme) {
        super(base);
        mTheme = theme;
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

    @Override
    public void setTheme(int resid) {
        if (mThemeResource != resid) {
            mThemeResource = resid;
            initializeTheme();
        }
    }

    public void setTheme(@Nullable Resources.Theme theme) {
        mTheme = theme;
    }

    @Override
    public int getThemeResId() {
        return mThemeResource;
    }

    @Override
    public Resources.Theme getTheme() {
        if (mTheme != null) {
            return mTheme;
        }

        mThemeResource = Resources.selectDefaultTheme(mThemeResource,
                getApplicationInfo().targetSdkVersion);
        initializeTheme();

        return mTheme;
    }

    private void initializeTheme() {
        final boolean first = mTheme == null;
        if (first) {
            mTheme = getResources().newTheme();
            final Resources.Theme theme = getBaseContext().getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme, mThemeResource, first);
    }
    
    ...
}

3、Activity以及Application创建过程

3.1、Activity创建过程

​ Activity创建在ActivityThread类的performLaunchActivity方法中,部分代码如下:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        ...

        if (activity != null) {
            ...
            
            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, r.configCallback,
                    r.assistToken);

            ...
            
            r.activity = activity;
        }
        r.setState(ON_CREATE);

        ...

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }

    return activity;
}

​ 以上代码可以简单概括如下:

​ 首先创建ContextImpl的实例appContext, 然后通过Instrumentation类的newActivity方法生成Activity,最后调用Activity的attach方法将appContext关联到Activity的mBase,从而将Activity中有关Context的操作委托给ComtextImpl类的实例。

3.2、Application创建过程

​ 和Activity一样,Application内部的mBase也是ContextImpl,创建Activity时会检查Application是否存在,不存在则创建。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);   
    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
        }
    }
    ...
}

​ 其中r.packageInfo为LoadApk的实例, makeApplication方法也会先创建一个ComtextImpl的实例appContext,然后调用Instrumentation的newApplication创建Application,并调用Application的attach方法将appContext关联到Application的mBase,将Application中有关Context的操作委托给ComtextImpl类的实例。部分代码如下:

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

    Application app = null;

    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }

    try {
        ...
        
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    ...

    return app;
}

4、总结

​ 除了显示Dialog、启动Activity或者加载View,应用中用到Context的地方都可以使用Application作为Context,这样可以避免内存泄露的情况。

posted @ 2021-03-25 19:20  笪笠  阅读(236)  评论(0编辑  收藏  举报