Android's world of Contexts

     Android's most frequently used APIs is based on Context and Activity, Service, Application basically expose these APIs but upon a closer look, Activity class basically extends ContextThemeWrapper (theme added functionality on top of ContextWrapper). ContextWrapper is just an implementation to delegate all context specific calls to an enclosed context. So what is the need for an Activity to relay these calls? This is to give flexibility to change context to serve different needs.

    In case of an application, android creates a context (ContextImpl) and attaches this to the Application instance and the Application instance is set as an outer context for the newly created context.

            ContextImpl appContext = new ContextImpl();
            appContext.init(this, null, mActivityThread);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);

This is also the case for an Activity.

        ContextImpl appContext = new ContextImpl();
        appContext.init(r.packageInfo, r.token, this);
        appContext.setOuterContext(activity);

        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);

   The attach() basically sets the specified Context as the enclosed context of the ContextWrapper. From here on, all context APIs via Activity are served by this enclosed context. This gives the flexibility to change the underlying context. Android uses this flexibility to show the Activity's content on a secondary display, should the package name be set in debug.second-display.pkg property.

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

No comments: