美文网首页
Context关联类和Application Context创建

Context关联类和Application Context创建

作者: code希必地 | 来源:发表于2020-09-11 15:35 被阅读0次

1、Context的概述

Context就是上下文或者场景,我们可以把它的使用场景大概分为2类:

  • 1、使用Context调用方法,比如打开Activity、启动服务、获取资源。
  • 2、调用方法传入Context,比如弹出吐司、弹出弹窗。
    Context是一个抽象类,它内部定义了很多静态变量和抽象方法,ContextImplContext的具体实现类,ContextWrapper虽然继承了Context但是具体的实现基本上全部交由其内部的成员mBaseContextImpl来完成。
    Application、Service、Activity都是间接继承Context的,所以一个程序中Context的数量==所有Activity和Service的个数加Application的个数(有几个进程就有几个Application)。下面给出Context的关系图。
    Context关系图.png
  • 1、ContextImplContextWrapper是继承Context,但是Context抽象方法是在ContextImp中实现的,ContextWrapper虽然也继承了Context但是其中的方法的实现都交由ContextImpl去完成的。ContextWrapper只是ContextImpl的包装类。
  • 2、ContextThemeWrapperServiceApplication都继承了ContextWrapper,所以它们也可以使用mBase调用Context中的方法,它们也是ContextImpl的包装类。
  • 3、Activity是继承ContextThemeWrapper,从名字就能看出它是和主题相关的,所以和主题相关的Activity是继承ContextThemeWrapper,而和主题无关的ServiceApplication是继承ContextWrapper

2、Application 的Context创建过程

我们一般通过getApplicationContext()来获取系统全局的Application Context,那么Application Context是在哪里创建的呢?
这就要从应用的启动开始,App启动会调用ActivityThread的内部类ApplicationThreadscheduleLaunchActivity方法来启动

private class ApplicationThread extends ApplicationThreadNative {
 ...
   @Override
    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
            int procState, Bundle state, PersistableBundle persistentState,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            updateProcessState(procState, false);
            ActivityClientRecord r = new ActivityClientRecord();
            ...
            sendMessage(H.LAUNCH_ACTIVITY, r);
    }
 ...   
}

ApplicationThreadscheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,目的是将启动Activity的逻辑放在主线程中的消息队列中,这样启动Activity的逻辑会在主线程中执行。在handleMessage()中调用ActivityThreadhandleLaunchActivity()
ActivityThread##handleLaunchActivity()

public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        //....
        final Activity a = performLaunchActivity(r, customIntent);
        //....
        return a;
    }

会调用performLaunchActivity()
ActivityThread##performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        //....
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            //....
            }
        return activity;
    }

这里只保留了和Application Context相关的逻辑,r.packageInfo返回的是LoadedApk的对象。
LoadedApk##makeApplication()

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    //...
    try {
      //...
       java.lang.ClassLoader cl = getClassLoader();
      //...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//分析1
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);//分析2
        appContext.setOuterContext(app);
    } catch (Exception e) {
       //...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;//分析3
    //...
    return app;
}
  • 1、分析1:创建ContextImpl的对象,其内部直接通过new ContextImpl()进行创建。
  • 2、分析2:通过InstrumentationnewApplication()创建Application
  • 3、分析3:Application赋值给LoadedApk中的mApplication。getApplicationContext()方法返回就是这个mApplication。
    先看下创建Application的方法
    Instrumentation##newApplication()
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是通过instantiateApplication()创建的
Instrumentation##instantiateApplication()

 public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }

可以看到最终是通过反射创建的,再回过来看下Instrumentation.newApplication(),在Application创建完成后调用了Applicationattach()
Application##attach()

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

里面又调用了Application的父类ContextWrapperattachBaseContext(context)
ContextWrapper##attachBaseContext()

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

这样就把创建的ContextImpl对象赋值给了ContextWapper中的mBase

2.1、Application Context的创建过程

Application Context的创建过程.png

3、Application Context的获取

Application Context的是通过getApplicationContext()返回的,
getApplicationContext()最终的实现在ContextWrapper
ContextWrapper##getApplicationContext()

@Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

可以看到最终会调用mBase.getApplicationContext()ContextImplgetApplicationContext()

@Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

从上面分析Application Context的创建过程可知,mPackageInfo 不为null,所以会调用mPackageInfo.getApplication(),最终会调用LoadedApk.getApplication()
LoadedApk##getApplication()

Application getApplication() {
        return mApplication;
    }

mApplicationmakeApplication中已经赋值,mApplication就是Application的实例。

4、getApplication()和getApplicationContext()

经过上面的分析可知getApplication()和getApplicationContext()返回的都是Application的实例。
既然他们返回完全一样,为什么要有这两个方法呢?
表面上的区别就是只有在Activity、Service中可以调用getApplication(),而在其他类中却只能使用context.getApplicationContext()来获取Application的实例。更深层的原因还没想到。

相关文章

网友评论

      本文标题:Context关联类和Application Context创建

      本文链接:https://www.haomeiwen.com/subject/tjnuektx.html