Android Context 解析

一个 Context 意味着一个场景,一个场景就是用户和软件进行交互的一个过程。那么从安卓程序的角度来看,Context 是什么?其实一个 Activity 就是一个 Context,一个 Service 也是一个 Context

Context 简介

Context 的类关系图如下:
Context 类关系图

Context 本身是一个抽象类,有两个直系子类,一个是 ContextWrapper,一个是 ContextImpl
从类名称上就可以看出,ContextWrapperContext 的代理类,而 ContextImpl 则是 Context 的实现类。另外 ContextWrapper 有三个直接的子类 ContextThemeWrapperServiceApplication,其中 ContextThemeWrapper 是一个带主题的封装类,而它有一个直接子类就是 Activity

Context 数量

一个应用程序中到底有多少个 Context 呢?
其实根据上面的 Context类型就已经可以得出答案了。Context 一共有 ApplicationActivityService 三种类型,因此一个应用程序中 Context 数量的计算公式就可以这样写:

Context 数量 = Activity 数量 + Service 数量 + 1

上面的 1 代表着 Application 的数量,因为一个应用程序中可以有多个 Activity 和多个 Service,但是只能有一个 Application。当然,这只是普通情况,不考虑多进程或其他因素的影响。

Context 功能

Context 到底可以实现哪些功能呢?这个就实在是太多了,弹出 Toast、启动 Activity、启动 Service、发送广播、操作数据库等都需要用到 Context
由于 Context 的具体能力是由 ContextImpl 类去实现的,因此在绝大多数场景下,ActivityServiceApplication 这三种类型的 Context 都是可以通用的。
不过有几种场景比较特殊,比如启动 Activity,还有弹出 Dialog。出于安全原因的考虑,Android 是不允许 ActivityDialog 凭空出现的。Activity 的启动必须要建立在另一个 Activity 的基础之上,也就是以此形成的返回栈。而 Dialog 则必须在一个 Activity 上面弹出(除非是 System Alert 类型的 Dialog),因此在这种场景下,只能使用 Activity 类型的 Context,否则将会出错。

Context 作用

要想知道 Context 的作用,首先先看一下 Context 提供的方法:

public abstract class Context {

    // 四大组件相关
    public abstract void startActivity(Intent intent);
    public abstract void startActivity(Intent intent, @Nullable Bundle options);
    public abstract void startActivities(Intent[] intents);
    public abstract void startActivities(Intent[] intents, Bundle options);
    public abstract void sendBroadcast(Intent intent);
    public abstract void sendBroadcast(Intent intent, @Nullable String receiverPermission);
    public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission);
    public abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
    public abstract void unregisterReceiver(BroadcastReceiver receiver);
    public abstract ComponentName startService(Intent service);
    public abstract ComponentName startForegroundService(Intent service);
    public abstract boolean stopService(Intent service);
    public abstract boolean bindService(Intent service, ServiceConnection conn, int flags);
    public abstract void unbindService(@NonNull ServiceConnection conn);
    public abstract ContentResolver getContentResolver();

    // 获取系统资源
    public abstract AssetManager getAssets();
    public abstract Resources getResources();
    public abstract PackageManager getPackageManager();
    public abstract ClassLoader getClassLoader();
    public abstract Context getApplicationContext();
    public abstract Context createConfigurationContext(Configuration overrideConfiguration);
    public abstract Context createDisplayContext(@NonNull Display display);
    public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass);

    // 获取应用资源
    public final CharSequence getText(@StringRes int resId);
    public final String getString(@StringRes int resId);
    public final String getString(@StringRes int resId, Object... formatArgs);
    public final int getColor(@ColorRes int id);
    public final Drawable getDrawable(@DrawableRes int id);
    public final ColorStateList getColorStateList(@ColorRes int id);
    public abstract void setTheme(@StyleRes int resid);
    public abstract Resources.Theme getTheme();
    public final TypedArray obtainStyledAttributes(@NonNull int[] attrs);
    public final TypedArray obtainStyledAttributes(@StyleRes int resid, int[] attrs);

    // 获取应用相关信息
    public String getOpPackageName();
    public Executor getMainExecutor();
    public abstract Looper getMainLooper();
    public abstract String getPackageName();
    public abstract ApplicationInfo getApplicationInfo();
    public abstract String getPackageResourcePath();
    public abstract String getPackageCodePath();
    public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
    public abstract int checkPermission(@NonNull String permission, int pid, int uid);
    public abstract int checkSelfPermission(@NonNull String permission);

    // 文件相关
    public abstract boolean deleteFile(String name);
    public abstract File getDataDir();
    public abstract File getFilesDir();
    public abstract File getNoBackupFilesDir();
    public abstract File getExternalFilesDir(@Nullable String type);
    public abstract File[] getExternalFilesDirs(String type);
    public abstract File getObbDir();
    public abstract File[] getObbDirs();
    public abstract File getCacheDir();
    public abstract File getCodeCacheDir();
    public abstract File getExternalCacheDir();
    public abstract File[] getExternalCacheDirs();
    public abstract File[] getExternalMediaDirs();
    public abstract SharedPreferences getSharedPreferences(String name, int mode);
    public abstract boolean moveSharedPreferencesFrom(Context sourceContext, String name);
    public abstract void reloadSharedPreferences();
    public abstract FileInputStream openFileInput(String name);
    public abstract FileOutputStream openFileOutput(String name, @FileMode int mode);

    // 数据库相关
    public abstract SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory f);
    public abstract boolean deleteDatabase(String name);
    public abstract String[] databaseList();

    // 其他
    public void registerComponentCallbacks(ComponentCallbacks callback);
    public void unregisterComponentCallbacks(ComponentCallbacks callback);

}

根据 Context 的源码可以发现,Context 就相当于 Application 的大管家,主要负责:

  • 四大组件的交互:包括启动 ActivityBroadcastService,获取 ContentResolver 等;
  • 获取系统资源:包括获取 AssetManagerPackageManagerResourcesSystem Service 等;
  • 获取应用资源:包括获取 colorstringdrawabletheme 等;
  • 文件相关:包括获取各种应用目录、SharedPreference、删除文件等;
  • 数据库相关:包括打开数据库、删除数据库、获取数据库路径等;
  • 其它辅助功能:比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生。

Context 区别

ContextWrapper

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {

    @UnsupportedAppUsage
    Context mBase; // 注意这个成员

    public ContextWrapper(Context base) {
        mBase = base;
    }

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     *
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

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

    @Override
    public PackageManager getPackageManager() {
        return mBase.getPackageManager();
    }

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

    ...

}

可以看到,ContextWrapper 实际上就是 Context 的代理类而已,所有的操作都是通过内部成员 mBase 完成的。另外,ActivityServicegetBaseContext() 方法返回的就是这个 mBase 对象。

ContextThemeWrapper

/**
 * A context wrapper that allows you to modify or replace the theme of the wrapped context.
 */
public class ContextThemeWrapper extends ContextWrapper {
    private int mThemeResource;
    private Resources.Theme mTheme;
    private LayoutInflater mInflater;
    private Configuration mOverrideConfiguration;
    private Resources mResources;

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

    /**
     * 在Recource初始化之前,传入配置信息
     */
    public void applyOverrideConfiguration(Configuration overrideConfiguration) {
        if (mResources != null) {
            throw new IllegalStateException(
                    "getResources() or getAssets() has already been called");
        }
        if (mOverrideConfiguration != null) {
            throw new IllegalStateException("Override configuration has already been set");
        }
        mOverrideConfiguration = new Configuration(overrideConfiguration);
    }

    @Override
    public Resources getResources() {
        return getResourcesInternal();
    }

    private Resources getResourcesInternal() {
        if (mResources == null) {
            if (mOverrideConfiguration == null) {
                mResources = super.getResources();
            } else if (Build.VERSION.SDK_INT >= 17) {
                // 根据配置信息初始化 Resource
                // 注意,这里创建了另一个和 Base Context 不同的 Resource
                final Context resContext = createConfigurationContext(mOverrideConfiguration);
                mResources = resContext.getResources();
            }
        }
        return mResources;
    }

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

    @Override
    public Resources.Theme getTheme() {
        // 只会初始化一次
    if (mTheme != null) {
            return mTheme;
        }

        if (mThemeResource == 0) {
            mThemeResource = R.style.Theme_AppCompat_Light;
        }
        initializeTheme();

        return mTheme;
    }

    /**
     * 初始化Theme
     */
    private void initializeTheme() {
        final boolean first = mTheme == null;
        if (first) {
            // 根据 Resource 获取 Theme
            mTheme = getResources().newTheme();
        // 复制内容
            Resources.Theme theme = getBaseContext().getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme, mThemeResource, first);
    }

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

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

    @Override
    public AssetManager getAssets() {
        return getResources().getAssets();
    }

}

结合注释及源码,可以发现,ContextThemeWrapper 内部包含了与主题相关的接口,有自己的 ThemeResource 成员,并且 Resource 可以传入自己的配置初始化。即 ThemeResource 相关的行为不再是直接调用 mBase 的方法了,也就说,ContextThemeWrapper 和它的 mBase 成员在 ResourceTheme 相关的行为上是不同的。

ContextImpl

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {

    private Resources mResources;
    private int mThemeResource = 0;
    private Resources.Theme mTheme = null;

    @Override
    public Resources getResources() {
        return mResources;
    }

    void setResources(Resources r) {
        if (r instanceof CompatResources) {
            ((CompatResources) r).setContext(this);
        }
        mResources = r;
    }

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

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

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

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

            return mTheme;
        }
    }

    /**
     * 初始化Theme
     */
    private void initializeTheme() {
        if (mTheme == null) {
            mTheme = mResources.newTheme();
        }
        mTheme.applyStyle(mThemeResource, true);
    }

    private static Resources createResources(...) {
        return ResourcesManager.getInstance().getResources(...);
    }

    static ContextImpl createSystemContext(... {
        LoadedApk packageInfo = new LoadedApk(mainThread);
        ContextImpl context = new ContextImpl(...);
        context.setResources(packageInfo.getResources());
        return context;
    }

    static ContextImpl createAppContext(...) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(...);
        context.setResources(packageInfo.getResources());
        return context;
    }

    static ContextImpl createActivityContext(...) {
        ContextImpl context = new ContextImpl(...);
        final ResourcesManager resourcesManager = ResourcesManager.getInstance();
        context.setResources(resourcesManager.createBaseActivityResources(...));
        return context;
    }

}

从代码中可以看出,ContextImpl 真正实现了 Context 中所有的方法,另外,ContextImpl 可以用于创建 ActivityService 以及 ApplicationmBase 成员。

不同组件创建 ContextImpl 的方式:

  • Activity:调用 ActivityThread.createBaseContextForActivity() 方法;
  • Service/Application:**调用 ContextImpl.createAppContext() 方法;
  • Provider:**调用 ContextImpl.createPackageContext() 方法;
  • BroadcastReceiver:**调用 Application.getBaseContext() 方法。

总结

Context 的继承关系如下:
Context 继承关系

  • ContextWrapperContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化;
  • ContextImplContext 的主要实现类,ActivityServiceApplicationBase Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身;
  • ActivityServiceApplicationBase Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类;
  • ServiceApplication 使用同一个 Recource,和 Activity 使用的 Resource 不同。

参考

关于Context不得不说的细节


评论
 上一篇
Java 中12个常见的语法糖 Java 中12个常见的语法糖
本文转载自:https://mp.weixin.qq.com/s/EBnM7QAOPjDk5bG3M0Mu-w 本文从 Java 编译原理角度,深入字节码及 class 文件,抽丝剥茧,了解 Java 中的语法糖原理及用法,帮助大家在学会如
2019-07-08
下一篇 
深入理解 Gradle 框架之二:依赖实现分析 深入理解 Gradle 框架之二:依赖实现分析
本文转载自:https://mp.weixin.qq.com/s/WQCqUaYDPHDIjUHlxjRIkw 引言大家在日常开发中,见过最多的可能就是下面3种依赖声明: implementation “org.jetbrains.kot
2019-06-26
  目录