Android中Context源码分析(一)

版权声明:本文为博主原创文章,欢迎转载但需注明出处谢谢! https://blog.csdn.net/dongxianfei/article/details/52303847

1、背景

作为Android开发人员相信大家对Context对象应该有所了解吧?我们平时无论是获取资源还是做其他操作,有时候都需要传入一个Context对象,但有时候我们传入Activity对象程序也没有报错抛异常,很多人就会感觉到奇怪。另外我们平时有时候会用到getApplication和getApplicationContext方法,但是二者又有什么区别呢?好啦,今天我们就来和大家聊聊Android的Context对象及其子类。

2、Context关系图

我们先来看一下Context类的源码介绍

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {

}

从注释可以看出,Context提供了应用环境的全局信息,允许获取应用程序的资源和类型,它是一个抽象类,Android为其提供了实现类,通过其实现类允许我们获取应用程序资源和类(比如启动Activity,接受Intent,发送广播等),另其有很多抽象方法,相当于提供了通用的API。

Context在Android中有很多子类,但在这里我们只分析日常使用较为广泛的子类。先看它们的关系图:

Context子类关系图

从图中可以看出,Context的直接子类是ContextImpl和ContextWrapper(后文分析二者的作用),Service和Application二者相似,都是继承于ContextWrapper,而Activity是继承于ContextThemeWrapper,因为Activity是有主题的。

接下来我们对照关系图来看源码。

3、源码分析

我们先来看看Context类的实现类ContextImpl类:

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

}

其实现了Context的所有方法。

在来看看Context类的另外一个子类:

/**
 * 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 {
    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;
    }

看这个类的类名大概就了解该类的作用,对了这个类就是Context类的包装类,该类内部包含一个ContextImpl类的引用mBase,所以现在应该知道ContextImpl类的作用了吧?就是Context类的真是实现者。在此引出一个设计模式——装饰者模式(ContextWrapper是Context的装饰者,而ContextImpl才是真正的实现者)。

接下来我们在来看看ContextWrapper的子类ContextThemeWrapper类:

/**
 * A ContextWrapper that allows you to modify the theme from what is in the 
 * wrapped context. 
 */
public class ContextThemeWrapper extends ContextWrapper {

}

该类内部包含设置主题的方法,即android:theme属性。

最后我们来看看Activity,Service和Application的源码:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

}

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {

}

public class Application extends ContextWrapper implements ComponentCallbacks2 {

}

以上就是Context及其常用子类的关系。

4、各种Context子类的创建过程

在之前的文章中我们说了Activity的启动过程Activity启动流程源码分析之入门(一),大家如果不是很清楚的可以先去阅读一下。

(1)Activity的ContextImpl实例化源码

通过startActivity方法启动新的Activity最终都会调用ActivityThread类的handleLaunchActivity方法,该方法内部会调用performLaunchActivity方法来创建Activity对象,我们进入该方法。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //创建Activity对象
    Activity activity = null;
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

        try {       
            //(1)这里调用LoadedApk的makeApplication
            Application app = r.packageInfo.makeApplication(false, 
mInstrumentation);

            //......
            if (activity != null) {
            //(2)创建Context对象
            Context appContext = createBaseContextForActivity(r, activity);
            //......

            //(3)将创建的appContext对象传入到Activity的attach方法中
            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);
        }
}

我们首先来看(1)处代码,此处通过LoadedApk的makeApplication方法来创建Application对象,我们后文分析。

(2)处通过createBaseContextForActivity方法传入当前的Activity对象来创建Context对象,我们进入该方法。

private Context createBaseContextForActivity(ActivityClientRecord r,
            final Activity activity) {
        //通过调用ContextImpl的静态方法createActivityContext来创建ContextImpl,我们后续会详细阐述该方法
        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
        //这里需要重点关注下面这句,ContextImpl类有一个成员变量mOuterContext,通过下面语句将Activity赋值给mOuterContext变量,是ContextImpl持有Activity的引用
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        //......
        //返回ContextImpl对象
        return baseContext;
}

(3)处将新创建的ContextImpl对象传递到Activity的attach方法。

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) {
        //此处调用父类ContextThemeWrapper的attachBaseContext方法并最终调用ContextWrapper类的attachBaseContext方法,将新创建的ContextImpl对象赋值给ContextWrapper的成员变量mBase,这样ContextWrapper及其子类的mBase成员变量就被实例化为ContextImpl对象。
        attachBaseContext(context);

        //......

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        //此处会将通过LoadedApk.makeApplication创建的Application对象赋值给mApplication变量
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;

        //......
}

由以上分析可以看出,Activity通过ContextWrapper的成员mBase来引用ContextImpl对象,即Activity组件可通过这个ContextImpl对象来执行一些具体的操作(启动Service等)。
同时ContextImpl类又通过自己的成员变量mOuterContext引用了与它关联的Activity,这样ContextImpl类也可以操作Activity。

因此说明一个Activity就有一个Context,而且生命周期和Activity类相同。

2、Service的ContextImpl实例化源码

我们启动Service一般是通过startService和bindService方式,但是无论通过哪种方式来创建新的Service其最终都是通过ActivityThread类的handleCreateService()方法来执行操作,我们来看源码。

private void handleCreateService(CreateServiceData data) {
    //创建Service对象
    Service service = null;
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance(); 

        //(1)通过ContextImpl的静态方法createAppContext创建ContextImpl对象
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

        //(2)和之前的Activity一样,ContextImpl中有一个成员变量mOuterContext,此处将Service对象赋值给mOuterContext,以便让ContextImpl内部持有一个Service对象的引用
        context.setOuterContext(service);

        //(3)这里调用LoadedApkmakeApplication
        Application app = packageInfo.makeApplication(false, mInstrumentation);

        //(4)这里调用Serviceattach方法
        service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
        service.onCreate();
        mServices.put(data.token, service);
}

(1)处通过调用ContextImpl的静态方法createAppContext来创建ContextImpl,我们后续会详细阐述该方法。
(2)处和之前的Activity一样,是ContextImpl对象持有当前Service的引用。
(3)处调用LoadedApk对象的makeApplication方法来创建Application对象,我们后文分析。
(4)此处调用Service的attach方法,我们进入该方法。

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        //这里调用父类的attachBaseContext方法
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        ////此处会将通过LoadedApk.makeApplication创建的Application对象赋值给mApplication变量
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

通过调用Service父类ContextWrapper的attachBaseContext方法使ContextWrapper的成员变量mBase被赋值给ContextImpl对象,既让ContextWrapper及其子类拥有ContextImpl对象的引用。
由此说明一个Service就有一个Context,而且生命周期和Service类相同。

3、Application的ContextImpl实例化源码

每个App都有一个全局的Application对象,并且与整个App的生命周期相同,创建Application的过程是在ActivityThread类的handleBindApplication()方法中,而ContextImpl的创建是在该方法中调运LoadedApk类的makeApplication方法,我们进入makeApplication方法。

public final class LoadedApk {

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        //当Application不为null时,直接返回该对象
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                initializeJavaContextClassLoader();
            }
            //(1)创建ContextImpl对象
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //(2)创建Application对象
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //(3)将新创建的Application对象赋值给ContextImpl对象的成员变量mOuterContext,既让ContextImpl内部持有Application对象的引用
            appContext.setOuterContext(app);
        } 

        //此处将创建的Application对象赋值给本类的mApplication变量
        mApplication = app;
        //......

        return app;
    }
}

(1)处通过ContextImpl的静态方法createAppContext来创建ContextImpl对象,我们后文分析。
(2)处通过调用Instrumentation对象newApplication方法来创建Application对象,我们进入该方法。

public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException,ClassNotFoundException {

        return newApplication(cl.loadClass(className), context);

    }

static public Application newApplication(Class<?> clazz, Context context)throws InstantiationException, IllegalAccessException,ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        //继续进入Application的attached方法
        app.attach(context);
        return app;
    }
 final void attach(Context context) {
        //终于看到了我们要找的attachBaseContext方法了
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

和之前一样,通过种种方法的传递,调用Application的父类方法attachBaseContext将ContextImpl对象赋值给ContextWrapper类的成员变量mBase,最终使ContextWrapper及其子类包含ContextImpl对象的引用。

(3)处将新创建的Application对象赋值给ContextImpl对象的成员变量mOuterContext,既让ContextImpl内部持有Application对象的引用。

这样就让二者之间都持有互相的对象,另外可说明一个Application就包含一个Context,而且生命周期和Application类相同,且于应用程序的生命周期相同。

5、Context访问资源的唯一性

通过我们前面分析Context的不同子类,我们知道有很多的Context对象,那我们平时在访问资源时getResources获得的资源是同一份吗?我们进入源码查看。

class ContextImpl extends Context {
    private final ResourcesManager mResourcesManager;
    private final Resources mResources;

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

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

我们平时通过getResources方法获取资源对象时,就是通过上面的方法获得ContextImpl的成员变量mResources,那它在哪里赋值的呢?

private ContextImpl(ContextImpl container, ActivityThread mainThread,LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,Display display, Configuration overrideConfiguration) {
    //......
    //单例模式获得ResourcesManager对象
    mResourcesManager = ResourcesManager.getInstance();

    //由于packageInfo只有一个,所以resources是同一份
    Resources resources = packageInfo.getResources(mainThread);
        if (resources != null) {
            if (activityToken != null
                    || displayId != Display.DEFAULT_DISPLAY
                    || overrideConfiguration != null
                    || (compatInfo != null && compatInfo.applicationScale
                            != resources.getCompatibilityInfo().applicationScale)) {
                //由于mResourcesManager是单例,所以resources是同一份
                resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
                        packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
                        packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
                        overrideConfiguration, compatInfo, activityToken);
            }
        }
        //最后赋值给mResources成员变量
        mResources = resources;
}

可以看出getResources获得的是同一份资源,另外在之前的Activity、Service还有Application的讲解中,他们都需要创建ContextImpl对象,他们创建该对象用的是createActivityContext和createAppContext方法,我们看下这两个方法。

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread,
                packageInfo, null, null, false, null, null);
    }


static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, IBinder activityToken) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        if (activityToken == null) throw new IllegalArgumentException("activityInfo");
        return new ContextImpl(null, mainThread,
                packageInfo, activityToken, null, false, null, null);
    }

无论哪种方法,最终都是调用ContextImpl的构造器来创建ContextImpl对象,所以他们使用的是同一份资源。

6、getApplication和getApplicationContext区别

还记得文章开头我们说要讨论这两个方法的区别吗?我们现在就从源码的角度来解析一下。

我们首先来看下getApplication方法,你会发现Context以及Application没有该方法,该方法是在Activity和Service中,我们看下源码。

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

    /** Return the application that owns this activity. */
    public final Application getApplication() {
        return mApplication;
    }
}

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
    /** Return the application that owns this service. */
    public final Application getApplication() {
        return mApplication;
    }
}

它们二者都提供了这个方法,并且返回值都是Application对象mApplication,那这个mApplication变量在什么地方赋值的呢?通过我们之前的Activity和Service的attached方法源码可知,此处的mApplication变量的值就是之前通过LoadedApk.makeApplication()方法的返回值。
所以不同的Activity和Service返回的Application均为同一个全局对象。

我们在来看看getApplicationContext方法,这个方法是定义在Context中,被其子类ContextImpl实现。

class ContextImpl extends Context {
 @Override
    public Context getApplicationContext() {
        //此处的mPackageInfo就是LoadedApk对象
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
}

public final class LoadedApk {
    Application getApplication() {
        return mApplication;
    }
}

由这个方法的源码可知,其返回的就是LoadedApk类的成员变量mApplication,而mApplication是在LoadedApk类的makeApplication方法中被赋值的。

从以上源码可知,getApplication()和getApplicationContext()返回的是同一个类型的变量。

好啦,今天的Context源码分析就到此为止啦,希望大家喜欢谢谢!

猜你喜欢

转载自blog.csdn.net/dongxianfei/article/details/52303847