Activity、Service、Application三者区别于关联

406 阅读8分钟

一、三者联系

首先我们来看一张图,从整体上了解三者的区别与联系:

几者关系.png

从上图我们可以看到,Service、Application直接继承于ContextWrapper,而ContextWrapper又是Context的子类。而Activity则是继承于ContextThemeWrapper,ContextThemeWrapper又是ContextWrapper的子类;从名字上看,ContextThemeWrapper是在ContextWrapper的基础上,新增了关于Theme主题的功能和逻辑,毕竟Service、Application不需要现实UI以及和用户交互,而这些职责都由Activity来承担了。 Service、Application和Activity都直接或间接的继承于ContextWrapper,并最终继承Context。 另外,Context是一个抽象类:

public abstract class Context {
    ......
}

它有两个子类,一个是ContextWrapper,另一个则是ContextImpl,而且,ContextImpl才是Context真正的实现类。ContextWrapper通过装饰模式,在内部维护并持有一个ContextImpl对象:mBase。

可以这样理解:ContextWrapper是对Context的逻辑进行封装,而ContextImpl则是对Context逻辑的具体实现。

而且Activity、Service以及Application通过getBaseContext()方法获取到的对象都是ContextImpl对象mBase:

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;
    }
    ......
}

这点那不难理解,Service、Application和Activity都是ContextWrapper直接或间接的子类;并且,都没有重写getBaseContext()方法。因此Service、Application和Activity调用getBaseContext()方法最终都会调用到同一个,获取到的mBase对象也是同一个。这一点,待会儿我们从他们各自的创建过程去理解。

在一个App应用中,Context的数量应有如下规则:

Context的数量 = Activity数量 + Service数量 + Application数量

上面公式中,Application数量取决于App应用是单进程还是多进程:

单进程的App应用,Application数量只有一个;
多进程的App应用,Application数量 = 进程数量。

二、Activity与ContextImpl(mBase)的关系。

Activity继承于ContextThemeWrapper。Activity的创建以及它与ContextImpl(mBase)的关系,还需要从Activity的创建过程说起。Activity的对象activity是在ActivityThread的performLaunchActivity()方法中出创建的:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ......
    ContextImpl appContext = createBaseContextForActivity(r);// 为Activity创建 Context 对象
    Activity activity = null;
    try {
        // 通过类加载器ClassLoader和 反射获取到Activity的对象
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 这里的 newActivity()方法后面我们会详细讲到。
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ......
    } catch (Exception e) {
        ......
    }
    // 通过 packageInfo 包信息,获取到当前应用对象 Application
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    if (activity != null) {
        ......
        // 将Activity和appContext上下文关联起来
        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);
        ......
    }
    return activity;
}

在这里,创建Activity对象之前,系统先是调用createBaseContextForActivity(r)方法创建ContextImpl对象appContext,然后通过对象appContext获取类加载器ClassLoader的对象cl,最后再通过对象mInstrumentation的newActivity()方法创建Activity对象。newActivity()方法内部其实是通过反射的方式创建Activity的,这里就不再进一步深入研究。有兴趣的伙伴们可以继续探寻。

createBaseContextForActivity(r)方法实现如下:

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    final int displayId;
    try {
        displayId = ActivityManager.getService().getActivityDisplayId(r.token);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
    ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
    ......
    return appContext;
}

在这里,很明显,ContextImpl还是调用了自己内部的static方法来创建自己,但该方法内部则是通过关键字“new”创建对象,然后设置resource资源:

static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, ActivityInfo activityInfo, 
        IBinder activityToken, int displayId, Configuration overrideConfiguration) {
    ......
    // 通过关键字“new”创建 ContextImpl 对象。
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName, activityToken, null, 0, classLoader);
    displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
    final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
            ? packageInfo.getCompatibilityInfo() : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;

    final ResourcesManager resourcesManager = ResourcesManager.getInstance();
    // 设置 resources资源
    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;
}

到这里,我们算是理清了Activity创建时,用到的Context对象本质上是其逻辑实现子类ContextImpl。

Activity创建成功之后,Activity通过attach()方法将Context与自己关联起来,在ActivityThread中,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, window, r.configCallback);

attach()方法第一个入参就是ContextImpl对象,在Activity内部,attach()方法对于这里传进去的ContextImpl对象appContext的处理如下:

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(context);// attach()方法的第一步就是在这里缓存Context对象
    ......
}

这里的attachBaseContext()方法实现如下:

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

在这里,我们可以看到attachBaseContext()方法是标有“@Override”注解的,表明这是一个复写自父类的方法,然后通过关键字“super”调用了父类的attachBaseContext()方法。而Activity的父类是ContextThemeWrapper,因此我们再进ContextThemeWrapper看看attachBaseContext()方法的实现:

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

很明显,ContextThemeWrapper是直接通过关键字“super”调用了父类的attachBaseContext()方法,而ContextThemeWrapper的父类则是ContextWrapper,ContextWrapper中对ContextImpl对象appContext做了一些缓存:

public class ContextWrapper extends Context {
    Context mBase;
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ......
    public Context getBaseContext() {
        return mBase;
    }
}

在这里,我们看到了ContextWrapper将我们从Activity传过来的ContextImpl对象appContext赋值给了Context对象mBase。然后当我们通过getBaseContext()方法获取Context对象时,系统直接将mBase对象返回给了调用端。这验证了我们本文开篇的说法。

三、Service与ContextImpl(mBase)的关系。

Service是直接继承于ContextWrapper。是在ActivityThread的的handleCreateService()方法中创建的,创建完成后,也会调用service.attach()方法:

private void handleCreateService(CreateServiceData data) {
    ......
    LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
    java.lang.ClassLoader cl = packageInfo.getClassLoader();
    Service service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    ......
    service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService());
    service.onCreate(); // service开始执行 onCreate()方法和逻辑
    ......
}

Service的创建与Activity的创建稍有不同,Service的创建过程,没有ContextImpl的参与,创建成功后,绑定Service与Context时,用到了ContextImpl。该对象是直接调用ContextImpl的createAppContext(this, packageInfo)方法创建的,实现过程与Activity中的ContextImpl对象稍有不同:

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
    ......
    ContextImpl对象的。
    
    context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, null);
    context.setResources(packageInfo.getResources());
    return context;
}

但其实最终还是调用同一个构造方法来创建ContextImpl对象的。 handleCreateService()方法中,service对象创建成功后,依然是调用了attach()方法来关联service对象与context:

service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService());

Service中attach()方法实现如下:

public final void attach(Context context, ActivityThread thread, String className, 
        IBinder token, Application application, Object activityManager) {
    attachBaseContext(context);
    ......
}

在这里,Service中attach()方法对Context对象的处理跟Activity的处理流程稍有不同:Activity内部是先调用了“super.attachBaseContext(newBase)”,然后处理了其他逻辑;而Service是直接调用父类的ContextWrapper中的attachBaseContext()方法。然后,剩下的逻辑跟Activity是一样的,就不再重复分析了。

四、Application与ContextImpl(mBase)的关系。

Application的创建始于SystemServer.Java的createSystemContext()方法,该方法会创建系统性的Context对象,然后调用startBootstrapServices()、startCoreServices()、startOtherServices()去启动各种服务。在createSystemContext()方法中,会调用ActivityThread.systemMain()方法创建ActivityThread对象,并调用activityThread.getSystemContext()方法创建 Context 对象mSystemContext。activityThread.getSystemContext()方法实现如下:

public final class SystemServer {
    private ContextImpl mSystemContext;
    ......
    private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);//设置系统主题。

        final Context systemUiContext = activityThread.getSystemUiContext();
        systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);//设置系统UI主题。
    }
    ......
}

可以看到 mSystemContext对象还是 ContextImpl类型的,他是通过调用 ContextImpl的 createSystemContext()方法创建的单例对象。

然后我们再回到 ActivityThread的systemMain()方法:

public static ActivityThread systemMain() {
    // 低内存设备上的系统进程不能使用硬件加速绘图,因为这会给进程增加太多开销。
    if (!ActivityManager.isHighEndGfx()) {
        ThreadedRenderer.disable(true);
    } else {
        ThreadedRenderer.enableForegroundTrimming();
    }
    ActivityThread thread = new ActivityThread();// 顺便创建了 mResourcesManager 资源管理对象;是整个Android的资源大管家。
    thread.attach(true, 0);
    return thread;
}

在这里,创建完ActivityThread之后,调用了attach()方法,该方法实现比较复杂,:我们捡跟本次分析相关的逻辑分析:

private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    // 此时 system = false,是从ActivityThread.java 的 main()方法传过来的。application用户应用进程才执行“if”分支
    if (!system) {
        ......
    // 此时 system = true,SystemServer.java 的 systemMain()方法传过来的。system_server进程才执行 “else” 分支
    } else {
        ......
        //根据LoadedApk对象来创建ContextImpl,对于system进程LoadedApk对象取值为mSystemContext。
        ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);
        mInitialApplication = context.mPackageInfo.makeApplication(true, null);
        mInitialApplication.onCreate();//初始化Application信息
        ......
    }
}

上面的mInitialApplication就是Application的对象,他是调用ContextImpl对象context的 LoadedApk类型的对象mPackageInfo的makeApplication()方法创建的。 LoadedApk中makeApplication()方法的实现:

public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    ......
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);// 通过反射创建 Application对象
    ......
}

在这里面,又去调用了ActivityThread对象mActivityThread中的Instrumentation类型的对象mInstrumentation的newApplication()方法,该方法内部通过反射创建的Application对象,而第三个入参传入的是ContextImpl类型的对象appContext:

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()方法,传入ContextImpl类型的对象appContext,Application的attach()方法实现如下:

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

上面context实质上是ContextImpl类型的对象,因为Application也是直接继承于ContextWrapper的,因此attach()方法中的第一步调用的attachBaseContext()方法跟Activity和Service调用的attachBaseContext()方法是同一个,因此传入的亦即保存的Context对象本质上都是ContextImpl类型的对象;因此:

在Application、Activity和Service中,我们通过getBaseContext()方法获取到的Context对象,本质上都是ContextImpl类型的对象。

追加结论:

1、在Activity和Service中,通过getApplication()方法获取到的application对象是同一个;
2、通过getApplicationContext()方法获取到的对象,从名字上看像是Context,实际上是一个application对象,它们也是同一个。
3、ContextWrapper通过装饰模式,内部持有并维护mBase对象;而且ContextWrapper内部主要起get()/set()作用,并且,每个get()/set()方法内部还是调用mBase对象对应的get()/set()方法来实现的。这里装饰模式的作用,跟代理模式很像。