阅读 319

Android 面试总结 - Context

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

有关应用程序环境的全局信息的接口。 这是一个抽象类,其实现由Android系统提供。 它允许访问特定于应用程序的资源和类,以及调用应用程序级操作,例如启动活动、广播和接收意图等。
摘自官方文档(Context 的类注释也是这个) Context

Context 也就是上下文对象,是 Android 常用的类。我们常用的 Activity,Service 和 Application 都间接的继承了 Context。Context 是一个抽象类

// android.content.Context
public abstract class Context
复制代码

日常开发中,使用 Context 的地方数不胜数,比如像弹 Toast、创建 Dialog、创建自定义 View 等等时候都需要传入 Context 对象,还有像启动 Activity、Service、访问资源和获取系统服务等等,都离不开 Context。所以 Android 同学必须掌握 Context 相关知识。

面试高频题:

  1. 应用中一共有几个 Context 对象
  2. Context、ContextImpl、ContextWrapper、ContextThemeWrapper 这几个类有什么关系?
  3. 说说 Application Context 的创建过程

应用中一共有几个 Context 对象

上面分析 Activity、Service 和 Application 都间接的继承了 Context。
空口无凭,凡事是要讲证据的。来来,我们这就来找找证据:

先来看看 Application

// 自定义的 Application
class MyApplication : Application() {}

// android.app.Application
public class Application extends ContextWrapper implements ComponentCallbacks2 {}

// android.content.ContextWrapper
public class ContextWrapper extends Context {}
复制代码

Application 继承了 Context 的包装类 ContextWrapper。这是一个 Context 对象。

Activity

// android.app.Activity
public class Activity extends ContextThemeWrapper

// android.view.ContextThemeWrapper
public class ContextThemeWrapper extends ContextWrapper

// android.content.ContextWrapper
public class ContextWrapper extends Context {}
复制代码

Activity 也继承了 Context 的包装类 ContextWrapper,所以每个 Activity 都是一个 Context 对象。

Service

// android.app.Service
public abstract class Service extends ContextWrapper

// android.content.ContextWrapper
public class ContextWrapper extends Context {}
复制代码

Service 也继承了 Context 的包装类 ContextWrapper,所以每个 Service 都是一个 Context 对象。

  1. 应用中一共有几个 Context 对象

回答:Activity 和 Service 的数量总数 + 1, 1 是 Application 的数量。

Context、ContextImpl、ContextWrapper、ContextThemeWrapper 这几个类有什么关系?

根据第一题计算 Context 数量的分析,能看出来的,Activity、Service、Application 都间接继承自 Context
Activity -> ContextThemeWrapper -> ContextWrapper -> Context
Service -> ContextWrapper -> Context
Application -> ContextWrapper -> Context

5419805-7132b241579bc866.webp

从图中我们可以看出,ContextImpl 和 ContextWrapper 继承自 Context, ContextWrapper 内部包含 Context 类型的 mBase 对象, mBase 具体指向 ContextImpl, ContextImpl 提供了很多功能,但是外界需要使用并拓展 ContextImpl 的功能,因此设计上使用了装饰模式,ContextWrapper 是装饰类,它对 ContextImpl 进行包装,ContextWrapper 主要是起到了方法传递的作用, ContextWrapper 中几乎所有的方法都是调用 ContextImpl 的相应方法来实现的。 ContextThemeWrapper、Service 和 Application 都继承自 ContextWrapper, 这样它们都可以通过 mBase 来使用 Context 的方法,同时它们也是装饰类,在ContextWrapper 的基础上又添加了不同的功能。ContextThemeWrapper 中包含和主题相关的方法(比如 getTheme 方法),因此,需要主题的 Activity 继承 ContextThemeWrapper,而不需要主题的 Service 继承 ContextWrapper。

Context 的关联类采用了装饰模式,主要有以下的优点:

  • 使用者(比如 Service )能够方便的使用 Context。
  • 如果 ContextImpl 发生变化,它的装饰类 ContextWrapper 不需要做任何修改。
  • ContextImpl 的实现不会暴露给使用者,使用者也不必关心 ContextImpl 的实现。
  • 通过组合而非继承的方式,拓展 ContextImpl 的功能,在运行时选择不同的装饰类,实现不同的功能。

以上两段内容摘自皇叔的《Android 进阶解密》

Application Context 的创建过程

Application 的 Context 是在 Activity 创建过程中创建的 Activity 创建流程中会走到 ActivityThread 的 handleLaunchActivity

// ActivityThread#handleLaunchActivity
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    ...
    final Activity a = performLaunchActivity(r, customIntent);
    ... 
    return a;
}
复制代码

调用 Activity 启动过程中的核心实现 performLaunchActivity

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    // app 的 Context 在这儿创建的
    ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            // 通过反射方式创建 Activity 对象
            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);
            }
        }
    ...
    // makeApplication 方法内部会判断 app 是否创建了 Application,若已创建则返回,若未创建则新建并返回
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    ...
    // attach 方法非常关键!!!内部创建了 PhoneWindow 对象
    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);
    ...
    // 这里开始 Attach 的生命周期 onCreate
    if (r.isPersistable()) {
          mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
          mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    ...
}
复制代码

Application app = r.packageInfo.makeApplication(false, mInstrumentation); 获取 Application 对象, r.packageInfo 是 LoadedApk 类型的对象。所以需要卡 LoadedApk 的 makeApplication。

第 1 步:判断 mApplication 对象不为空 则直接返回已创建的 mApplication 对象。
第 2 步:获取 app 的 Application 的类名
第 3 步:如果没有设置过 Application 则会默认为 android.app.Application
第 4 步:通过 ContextImpl.createAppContext 创建 ContextImpl
第 5 步:通过 mInstrumentation.newApplication 创建 Application 对象
第 6 步:创建的 Application 对象赋值给 mApplication 缓存下来
第 7 步:调用 instrumentation 的 callApplicationOnCreate 来调用 Application 的 onCreate 方法
第 8 步:返回刚创建的 Application 对象

// android.app.LoadedApk#makeApplication
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    // 1
    if (mApplication != null) {
        return mApplication;
    }

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

    Application app = null;
    
    // 2
    String appClass = mApplicationInfo.className;
    // 3
    if (forceDefaultAppClass || (appClass == null)) {
        // 如果没有设置过 Application 则会默认为 android.app.Application
        appClass = "android.app.Application";
    }

    try {
        final java.lang.ClassLoader cl = getClassLoader();
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
                false, false);
        for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
        }

        // 4
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // The network security config needs to be aware of multiple
        // applications in the same process to handle discrepancies
        NetworkSecurityConfigProvider.handleNewApplication(appContext);
        // 5
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        if (!mActivityThread.mInstrumentation.onException(app, e)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + ": " + e.toString(), e);
        }
    }
    mActivityThread.mAllApplications.add(app);
    // 6
    mApplication = app;

    if (instrumentation != null) {
        try {
            // 7
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // 8
    return app;
}
复制代码

先来看看 第 4 步:通过 ContextImpl.createAppContext 创建 ContextImpl 创建了 ContextImpl 对象并返回

// ContextImpl#createAppContext
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
        String opPackageName) {
    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
            0, null, opPackageName);
    context.setResources(packageInfo.getResources());
    context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
    return context;
}
复制代码

第 5 步:通过 mInstrumentation.newApplication 创建 Application 对象

// Instrumentation#newApplication
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}
复制代码

通过 AppComponentFactory 来创建 Application 对象

// AppComponentFactory#instantiateApplication
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
        @NonNull String className)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (Application) cl.loadClass(className).newInstance();
}
复制代码

最终是通过反射方式创建的 Application 对象。创建完 Application 对象后紧接着调用了 Application 对象 attach,传入了 LoadedApk#makeApplication 方法中第 4 步:通过 ContextImpl.createAppContext 创建的 ContextImpl 对象。

// Application#attach
final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
复制代码

调用了 attachBaseContext 将 ContextImpl 对象 context 传进去 attachBaseContext 是 Application 的父类 ContextWrapper 的方法

public class ContextWrapper extends Context {

    Context mBase;

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
}
复制代码

将 ContextImpl 类型对象 base 赋值给 mBase。

第 7 步:调用 instrumentation 的 callApplicationOnCreate 来调用 Application 的 onCreate 方法

// Instrumentation#callApplicationOnCreate
public void callApplicationOnCreate(Application app) {
    app.onCreate();
}
复制代码

内部调用了 Application 对象的 onCreate 方法。

到此 Application 的创建流程已经梳理完了。相信我们通过 Applicaton 的创建过程会加深对 Context 的理解,包括 Context、ContextImpl 和 ContextWrapper。

文章分类
Android
文章标签