各种Context及其初始化流程

QuibblerQuibbler 2020-07-13 约 28 分钟 2539 次阅读

各种Context及其初始化流程

Context在Android开发中实在是太重要的,用到的地方数不胜数。开发过程中没有Context是万万不能的,给人感觉Context简直是“万能”的。因为总有一些地方会用到Context,可以通过传参的方式传入;但是有些又没法通过传参传入,这时候可以使用全局ApplicationContext的方式获取到一个Context(见《如何获取全局Context?》)。总的来说有两种用法:

①调用Context的方法。比如startActivity()、getSystemService()、bindService()等等

②当做参数传给需要的构造函数或者方法。比如new Dialog(this)、LayoutInflater.from(context)等等



1、Context簇

在开发过程中,大部分场景总能随时随地的拿到Context使用,而且Context中封装了非常多的方法和常量直接使用,这一点非常方便。得益于Context类族有较好的设计。


Context本身是抽象类,直接实现有两个:ContextImpl和ContextWrapper。ContextImpl是Context的实现类。ContextWrapper内部成员变量mBase初始化后其实就是ContextImpl。ContextWrapper对Context类进行包装(装饰模式),真正的实现还是通过mBase调用ContextImpl中实现的方法。

四大组件中两个有生命周期的组件:Activity、Service都直接或间接继承了ContextWrapper。Service是“虚无”的,肉眼看不见,不需要主题,直接继承自ContextWrapper。而Activity因为有主题有所不同,所以又由ContextThemeWrapper继承自ContextWrapper封装了一层。

2、Application Context

一个应用的全局Application Context是如何被创建并初始化的呢?上回说到Activity的启动流程(参考《Activity启动流程源码分析》),走到ActivityThread的performLaunchActivity(...)方法中:

/**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
        } catch (Exception e) {
            
        }
        return activity;
    }

ActivityClientRecord是ActivityThread的静态内部类,其中packageInfo成员是LoadedApk类型。makeApplication(...)方法:

public Application makeApplication(boolean ,Instrumentation ) {
        if (mApplication != null) {
            return mApplication;
        }
        Application app = null;
        ...
        try {
            java.lang.ClassLoader cl = getClassLoader();
            ...
            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;
    }

方法中调用ContextImpl的createAppContext(ActivityThread mainThread, LoadedApk packageInfo)方法创建ContextImpl对象

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return createAppContext(mainThread, packageInfo, null);
    }
    
    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, 0,
                null, opPackageName);
        context.setResources(packageInfo.getResources());
        return context;
    }

再通过Instrumentation的newApplication(...)创建构造Application对象

/**
     * 执行流程的{@link Application}对象的实例化。 默认实现提供正常的系统行为。
     */
    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;
    }

其中调用Application的attach(Context )方法

/* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

将传入的ContextImpl对象设置给当前Application中的成员变量mBase。

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

别忘了,Application类继承自ContextWrapper,attachBaseContext(Context base)定义在父类ContextWrapper中。至此,Application的Context初始化完成,可以在Application中使用Context。

3、Activity Context

继续Activity的启动流程(参考《Activity启动流程源码分析》),同样是走到ActivityThread的performLaunchActivity(...)方法,这次关注的代码段和Application Context创建不一样不一样:

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);
            }
        }
        ...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
            if (activity != null) {
                ...
                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,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
                ...
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ...
            }
            ...
        } 
        ...
        return activity;
    }

方法中有几个关键的地方:

首先,调用本地方法createBaseContextForActivity()创建ContextImpl类型的变量appContext:

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

其次,通过Instrumentation类的newActivity(ClassLoader cl, String className,Intent intent)方法load创建activity。

再调用ContextImpl的setOuterContext(Context context)方法,将创建的activity传给ContextImpl类中的mOuterContext成员变量,这样ContextImpl也就反过来持有Activity,可以访问Activity成员。

final void setOuterContext(Context context) {
        mOuterContext = context;
    }

最后关键的一步,来看调用了Activity的attach(...)方法,将ContextImpl赋值给Activity父类中的mBase,以及进行一系列配置变量的初始化。

final void attach(Context context, ...) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mAssistToken = assistToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        ...
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
        mWindow.setColorMode(info.colorMode);
        ...
    }

方法中其它的都先不管,看第一行,调用了Activity中的attachBaseContext(context)方法

protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        if (newBase != null) {
            newBase.setAutofillClient(this);
            newBase.setContentCaptureOptions(getContentCaptureOptions());
        }
    }

然后调用了父类ContextThemeWrapper的attachBaseContext(context)方法

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

最后调用ContextWrapper中的attachBaseContext(context)方法,完成mBase成员变量的初始化,所有的Context实现都有mBase类也就是ContextImpl来实现,ContextWrapper当做中间件。

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

到这里,完成了Activity的Context初始化。

4、Service Context

继续Service的启动流程(参考《Service启动流程分析》), ActivityThread中的scheduleCreateService(...)方法向mH发送了消息

public final void scheduleCreateService(IBinder token,
            ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
        updateProcessState(processState, false);
        CreateServiceData s = new CreateServiceData();
        s.token = token;
        s.info = info;
        s.compatInfo = compatInfo;
        sendMessage(H.CREATE_SERVICE, s);
    }

通过handleCreateService(CreateServiceData data)方法创建Service:

private void handleCreateService(CreateServiceData data) {
        ...
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
        }
        ...
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
            ...
        }
        ...
    }

通用是通过ContextImpl的createAppContext(...)方法创建ContextImpl类型Context,并且将当前Service使用setOuterContext()方法让ContextImpl反向持有Service,最后调用Service的attach(...)方法

public final void attach(...) {
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

mBase是ContextImpl类型,持有了Service。Service又继承自ContextWrapper内部含有mBase,可以在Service调用所有Context中的方法。这样的设计让程序开发过程中几乎随处可以使用Context。

参考资料:

        Context详解

        一篇文章让你彻底弄懂Context到底是什么,不懂还怎么做Android开发?

        Context都没弄明白,还怎么做Android开发?

        Android Application中的Context和Activity中的Context的区别

        8年老Android开发谈Context

        

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w