55468afb207829188faa5d7ad3823f16
扒一扒 Context

Android 复习笔记目录

  1. 唠唠任务栈,返回栈和生命周期
  2. 唠唠 Activity 的生命周期
  3. 扒一扒 Context
  4. 为什么不能使用 Application Context 显示 Dialog?

本文永久更新地址: https://xiaozhuanlan.com/topic/0367548912

目录

  • 什么是 Context?
  • 四大组件和 Context
  • Application 和 Context
  • 为什么 Application 的 Context 不可以创建 Dialog ?
  • 未完待遇...

文章开头,先来看一段代码:

public class ContextActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_context);

        Log.e("context", "getApplication in Activity: " + getApplication().getClass().getName());
        Log.e("context", "getApplicationContext in Activity: " + getApplicationContext().getClass().getName());
        Log.e("context", "getBaseContext in Activity: " + getBaseContext().getClass().getName());

        startService(new Intent(this,ContextService.class));
    }
}

你能准确的说出这三行打印语句的执行结果吗?如果不能,你需要认真阅读这篇文章。

什么是 Context ?

Context 是一个抽象类。既然是抽象类,那么它就代表了一类具体对象的通用特征。先来看一下 Context 的类图:

其中看到了我们很熟悉的 ActivityServiceApplication,这些都是 Context 的具体实现类,也就是说 Context 抽象了这些类的通用特征和功能:

  • 获取系统资源,getResources()getAssets()
  • 启动各种系统组件
  • 获取系统服务
  • ......

这些与系统环境息息相关的功能都是由 Context 提供的,所以一般将其称为 上下文,它其实就是对当前运行环境的具体描述,为系统组件的正常运行提供必要的环境和资源。

在上面的类图中,可能有两个读者比较陌生的类,ContextWraaperContextImpl

ContextImpl 很好理解,它就是 Context 的具体实现类。Context 类中的所有抽象方法都是在 ContextImpl 中实现的。

class ContextImpl extends Context {
    ......

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

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

    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }

    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

    @Override
    public Looper getMainLooper() {
        return mMainThread.getLooper();
    }

    ......
}

ContextWraaper 其实也很简单,直接看它的实现代码:

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

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

    /**
     * 在这个方法中给 mBase 赋值
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    public Context getBaseContext() {
        return mBase;
    }

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

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

    ......
}

这是一个典型的 装饰者模式,也叫做 修饰模式,一下来自维基百科:

修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。

修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。

当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,类不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味着要为每一种组合创建一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。一个修饰模式的示例是JAVA里的Java I/O Streams的实现。

Context 是基本的抽象类,无论是实现类,还是装饰类,都直接或间接的实现它。ContextImpl 是 Context 的直接实现类,但各个组件并不是直接继承 ContextImpl,而是通过装饰类 ContextWrapper 来持有 ContextImpl。这是为什么呢?对于 Activity 和 Service 来说,它们都需要系统上下文运行环境,但它们又是不同的。Activity 需要显示到前台,它有页面,它需要主题,于是有了继承自 ContextWrapper 的 ContextThemeWrapper,扩展了功能,给 Activity 提供了主题。同时,Activity、Service、Application 这些具体组件本身又扩展出了不同的生命周期功能。

所以,装饰器模式通过组合和扩展装饰类,来给不同的具体对象提供了不同的功能扩展。

ActivityServiceApplication 最终都是继承自装饰类 ContextWrapperContextWrapper 通过 attachBaseContext() 方法来获取实际做事的 ContextImpl 对象。 所以这些组件的创建过程中,一定会在某一时机调用 attachBaseContext() 方法对 mBase 对象进行赋值,让我们从源码里面找找答案。

四大组件和 Context

Activity 和 Context

先说 Activity,Activity 的启动过程极其复杂,我们就直接从 ActivityThreadperformLaunchActivity() 方法看起。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            // 1. 获取 LoadedApk 对象
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ......

        // 2. 创建 ContextImpl 对象
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            // 3. 反射创建 Activity 对象
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ......
        } catch (Exception e) {
            ......
        }

        try {
            // 4. 创建 Application 对象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);


            if (activity != null) {
                ......
                appContext.setOuterContext(activity);
                // 5. 绑定 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, r.configCallback);

                ......
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    // 设置主题
                    activity.setTheme(theme);
                }

                // 6. 回调 onCreate()
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......
                r.activity = activity;
            }
            r.setState(ON_CREATE);

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
           ......
        }
        return activity;
    }

整理一下大致的执行流程:

  1. 获取 LoadedApk 对象,表示加载过的 Apk ,通常一个 App 对应着一个 LoadedApk
  2. 通过 createBaseContextForActivity() 方法创建 ContextImpl 对象
  3. 反射创建 Activity 对象
  4. 创建 Application 对象,这里也是用的反射。如果开发者没有声明自己的 Application 的话,就是默认的 androoid.app.Application
  5. 调用 activity.attach() ,这个方法很重要,后面详细说
  6. 回调 onCreate()

接着就是 Activity 正常的生命周期流程了。

重点看一下 createBaseContextForActivity() 方法和 attach() 方法。

    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        ......
        return appContext;
    }

调用了 ContextImpl.createActivityContext() 方法。

   static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        ......
        // 创建 ContextImpl 对象
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);

        ......

        final ResourcesManager resourcesManager = ResourcesManager.getInstance();
        context.setResources(resourcesManager.createBaseActivityResources(activityToken,
                packageInfo.getResDir(),
                splitDirs,
                packageInfo.getOverlayDirs(),
                packageInfo.getApplicationInfo().sharedLibraryFiles,
                displayId,
                overrideConfiguration,
                compatInfo,
                classLoader));
        context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                context.getResources());
        return context;
    }

装饰类 ContextWrapper 真正需要的 ContextImpl 对象现在已经创建出来了,但是还没有绑定到 Activity 。继续看 Activity.attach() 方法,注意 attach() 方法的第一个参数就是刚刚创建出来的 ContextImpl 对象。

```java
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, ActivityConfigCallback activityConfigCallback) {
// 回调 attachBaseContext()
attachBaseContext(context);

    ......

    // 创建 PhoneWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
top Created with Sketch.