Context是什么

                                                                        Context

   Context,翻译是上下文环境,在android应用开发中的地位举足轻重,甚至源码也有一句话:everything needs a context(看到过,但是忘了在哪里了)。


Context类图结构

从这个类图中我们看到,Context是一个抽象类,一边是Context的具体实现ContextImpl,一边则是组件的封装类ContextWrapper,便于二者各司其职,我理解是接口和实现分开,这样比较符合开闭原则吧。

  既然要搞清楚Context是干嘛的,得先看Context的实例什么时候创建的。这里有两个我们经常接触的组件,Activity和Service,以及用的较少的Application,它们的关系如下:

    1.Application:保存应用的全局信息的类,它的实例在应用创建的时候被创建,生命周期最长,应用销毁了才会被销毁。

    2.Activity:创建一个Activity便对应一个Context的实例,生命周期随着Activity的周期变化。

    3.Service:创建一个Service便对应一个Context的实例,生命周期随着Service的周期变化。

下面逐一看看它们是什么时候实例Context类的,我们需要知道,一个ContextWrapper类对应一个ContextImpl类的实例,因为具体实现的地方还是在ContextImpl中,ContextWrapper中的mBase变量指向ContextImpl变量的实例。


(1) Application的Context创建:

如图:


Application的创建过程

  跳过Process创建过程,在我们的应用被zygote进程孵化出来后,被反射调用main函数,这里会创建ActivityThread类的实例,该类并不是一个线程类的子类,但是它的main函数跑在新创建的进程的主线程。 创建后,调用attach方法,与AMS绑定:

```java

private void attach(boolean system) {

    if (!system) {

        final IActivityManager mgr = ActivityManager.getService();

        try {

            mgr.attachApplication(mAppThread);  // final ApplicationThread mAppThread = new ApplicationThread();

        } catch (RemoteException ex) {

            throw ex.rethrowFromSystemServer();

        }

    }

}

```

这里传入的mAppThread变量为ApplicationThread类,它为ActivityThread的内部类,作用就是作为应用端的binder服务端,负责接收AMS的调度,这里我们传入这个服务端的句柄,让AMS持有,从而AMS可以通过这个句柄来操控应用的行为。我们向AMS请求,则是通过AMS的句柄。接下来就到了AMS的逻辑:

@Override

public final void attachApplication(IApplicationThread thread) {

   synchronized (this) {

       int callingPid = Binder.getCallingPid();

       final long origId = Binder.clearCallingIdentity();

       attachApplicationLocked(thread, callingPid);

       Binder.restoreCallingIdentity(origId);

   }

}

-----------------------------------------------------------------------

   private final boolean attachApplicationLocked(IApplicationThread thread,

           int pid) {

           ProcessRecord app;  //app进程的各种信息

           app.makeActive(thread, mProcessStats);  //将IApplicationThread对象保存到ProcessRecord中

           if (app.instr != null) {

               thread.bindApplication(processName, appInfo, providers,  //通知客户端创建Application对象

                       app.instr.mClass,

                       profilerInfo, app.instr.mArguments,

                       app.instr.mWatcher,

                       app.instr.mUiAutomationConnection, testMode,

                       mBinderTransactionTrackingEnabled, enableTrackAllocation,

                       isRestrictedBackupMode || !normalMode, app.persistent,

                       new Configuration(getGlobalConfiguration()), app.compat,

                       getCommonServicesLocked(app.isolated),

                       mCoreSettingsObserver.getCoreSettingsLocked(),

                       buildSerial);

           } else {

               thread.bindApplication(processName, appInfo, providers, null, profilerInfo,

                       null, null, null, testMode,

                       mBinderTransactionTrackingEnabled, enableTrackAllocation,

                       isRestrictedBackupMode || !normalMode, app.persistent,

                       new Configuration(getGlobalConfiguration()), app.compat,

                       getCommonServicesLocked(app.isolated),

                       mCoreSettingsObserver.getCoreSettingsLocked(),

                       buildSerial);

           }

       return true;

   }

    在ApplicationThread的实现端中,就是把跨进程传递过来的各种数据再用一个AppBindData类保存下来,然后调用Handler对象H发送消息BIND_APPLICATION,最后在app线程中处理此逻辑,让AMS线程可以不必同步阻塞。接下来就到了handleBindApplication:

private void handleBindApplication(AppBindData data) {

   mBoundApplication = data;

   final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);

   updateLocaleListFromAppContext(appContext,

           mResourcesManager.getConfiguration().getLocales());

   if (ii != null) {

       final ApplicationInfo instrApp = new ApplicationInfo();

       ii.copyTo(instrApp);

       instrApp.initForUser(UserHandle.myUserId());

       final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,

               appContext.getClassLoader(), false, true, false);

       final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

       try {

           final ClassLoader cl = instrContext.getClassLoader();

           mInstrumentation = (Instrumentation)   //创建Instrumentation类对象

               cl.loadClass(data.instrumentationName.getClassName()).newInstance();

       } catch (Exception e) {

           throw new RuntimeException(

               "Unable to instantiate instrumentation "

               + data.instrumentationName + ": " + e.toString(), e);

       }

       final ComponentName component = new ComponentName(ii.packageName, ii.name);

       mInstrumentation.init(this, instrContext, appContext, component,

               data.instrumentationWatcher, data.instrumentationUiAutomationConnection);  //初始化Instrumentation

   try {

       // If the app is being launched for full backup or restore, bring it up in

       // a restricted environment with the base application class.

       Application app = data.info.makeApplication(data.restrictedBackupMode, null);  //创建Application类对象

       mInitialApplication = app;

       }

}

    Instrumentation类是用于管理Acitivty的工具类。这又有一个比较重要的对象出现,LoadedApk,它里面保存了类加载器ClassLoader,data.info的对象便是它,makeApplication函数中的逻辑:

public Application makeApplication(boolean forceDefaultAppClass,

       Instrumentation instrumentation) {

   if (mApplication != null) {  //保证唯一性,不会重复创建Application对象,所以我们可以认为Application对象是单例的

       return mApplication;

   }

   Application app = null;

   String appClass = mApplicationInfo.className;

   try {

       java.lang.ClassLoader cl = getClassLoader();  //获取类加载器实例

       ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); //创建ContextImpl实例

       app = mActivityThread.mInstrumentation.newApplication(   //实际创建Application对象的地方

               cl, appClass, appContext);  

       appContext.setOuterContext(app);

   } catch (Exception e) {

       if (!mActivityThread.mInstrumentation.onException(app, e)) {

           Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

           throw new RuntimeException(

               "Unable to instantiate application " + appClass

               + ": " + e.toString(), e);

       }

   }

   mActivityThread.mAllApplications.add(app);

   mApplication = app;

   if (instrumentation != null) {

       try {

           instrumentation.callApplicationOnCreate(app);   //Application的onCreate()被调用

       } catch (Exception e) {

           if (!instrumentation.onException(app, e)) {

               Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

               throw new RuntimeException(

                   "Unable to create application " + app.getClass().getName()

                   + ": " + e.toString(), e);

           }

       }

   }

   return app;

}

  这里我们看到了ContextImpl的创建时机,就是在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();

   app.attach(context);

   return app;

}

  如此就完成了Application的创建,并且调用attach方法把Application的mBase对象赋值给创建的ContextImpl,至此Application的创建就完成了,getApplicationContext() 返回的便是此处我们创建的Application类的对象。


  这里为什么要去LoadedApk类中利用类加载器,把对象创建出来呢?因为我们的Application类是可以自己拓展的,创建的时候是不确定应用自己是否复写了Application对象,利用类加载器就可以动态决定创建什么类的对象了,我们只需要从PMS中获取到application的具体类名即可,这个类名是写在Mainfest文件中的,后面Activity也是利用这种机制去创建对象。

(2)Actvity的启动过程,网上非常多文章分析了,我也就不再重复罗列出来了,大概的过程就是startActivity()被调用,然后检查目标Acitivity的进程是否创建,没创建就先向Zygote进程请求fork应用进程,然后一系列初始化过程后,最后在AMS模块中的ActivityStackSupervisor中realStartActivityLocked()调用IApplicationThread代理类,调用到scheduleLaunchActivity(),然后在应用的线程开始执行handleLaunchActivity(),最主要的逻辑在performLaunchActivity:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

   ActivityInfo aInfo = r.activityInfo;

   if (r.packageInfo == null) {

       r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,

               Context.CONTEXT_INCLUDE_CODE);

   }

   ContextImpl appContext = createBaseContextForActivity(r);  //创建ContextImpl对象实例

   Activity activity = null;

   try {

       java.lang.ClassLoader cl = appContext.getClassLoader();

       activity = mInstrumentation.newActivity(              //Activity对象创建

               cl, component.getClassName(), r.intent);

       StrictMode.incrementExpectedActivityCount(activity.getClass());

       r.intent.setExtrasClassLoader(cl);

       r.intent.prepareToEnterProcess();

       if (r.state != null) {

           r.state.setClassLoader(cl);

       }

   } catch (Exception e) {

       if (!mInstrumentation.onException(activity, e)) {

           throw new RuntimeException(

               "Unable to instantiate activity " + component

               + ": " + e.toString(), e);

       }

   }

   try {

       Application app = r.packageInfo.makeApplication(false, mInstrumentation); //应用已经创建过Application了,返回之前的对象

       if (activity != null) {

           appContext.setOuterContext(activity);

           activity.attach(appContext, this, getInstrumentation(), r.token,   //初始化activity

                   r.ident, app, r.intent, r.activityInfo, title, r.parent,

                   r.embeddedID, r.lastNonConfigurationInstances, config,

                   r.referrer, r.voiceInteractor, window, r.configCallback);

           if (r.isPersistable()) {

               mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);  //oncreate调用时机

           } else {

               mInstrumentation.callActivityOnCreate(activity, r.state);

           }

           if (!r.activity.mFinished) {

               if (r.isPersistable()) {

                   if (r.state != null || r.persistentState != null) {

                       mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, //如果之前保存了数据,可以在此恢复

                               r.persistentState);

                   }

               } else if (r.state != null) {

                   mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);

               }

           }

       }

   return activity;

}

  至于Activity的生命周期后面怎么走的,这里不在乎,我们只看Context实例化过程。同样的,这里也会创建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,

       Window window, ActivityConfigCallback activityConfigCallback) {

   attachBaseContext(context);   //同样的,会调用此函数为ContextWrapper中的mBase对象赋值一个创建的ContextImpl对象

   mWindow = new PhoneWindow(this, window, activityConfigCallback);  //创建PhoneWindow,这是对window操作的一系列操作的封装

   mWindow.setWindowControllerCallback(this);

   mWindow.setCallback(this);                    //设置callback是这个activity,有window相关的变化可以通知到这个activity

   mWindow.setOnWindowDismissedCallback(this);

   mWindow.getLayoutInflater().setPrivateFactory(this);

   if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {

       mWindow.setSoftInputMode(info.softInputMode);

   }

   mUiThread = Thread.currentThread();

   mMainThread = aThread;

   mInstrumentation = instr;

   mToken = token;

   mIdent = ident;

   mApplication = application;

   mIntent = intent;

   mReferrer = referrer;

   mComponent = intent.getComponent();

   mActivityInfo = info;

   mTitle = title;

   mParent = parent;

   mEmbeddedID = id;


   mWindow.setWindowManager(            

           (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

           mToken, mComponent.flattenToString(),

           (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

   if (mParent != null) {

       mWindow.setContainer(mParent.getWindow());

   }

   mWindowManager = mWindow.getWindowManager();

   mCurrentConfig = config;

   mWindow.setColorMode(info.colorMode);

}

  在attach函数中,也会为ContextWrapper中的mBase对象赋值一个ContextImpl对象,我们这里也能猜想到Service的创建估计也会有这个过程,而事实上Service的创建差不多也是这个过程,所以不再赘述了。


    我们可以知道Context例的实例化都是在我们在Application,Activity和Service创建的时候会实例化,而这些组件的父类都是ContextWrapper,所以在创建的时候还会先创建一个ContextImpl类对象,然后给自己的父类mBase变量赋值,既然如此,Context的引用对应的就是Application,Activity和Service了,Context的用处就是提供了一组抽象的函数,让子类去相对应的实现,当你持有一个Context引用的时候,你可以通过这个引用去获取相对应组件的信息。比如持有一个Activity的Context引用,你可以在别的地方调用startActivity()去启动一个新的activity。

总结:

  1.Context是抽象类,所以实例化要在子类中,Application,Service,Activity是我们实例化的地方,一般应用创建会实例化一个Application类,Service和Activity则是在startService和StartActivity被调用的时候会实例化,它们都会创建一个ContextImpl类实例,给自己的父类ContextWrapper的mBase变量赋值。

  2.Context是组件中通用操作的一组集合,当具体的子类实例化后,可以在别的地方通过保存一个Context引用去获取信息和通用操作等,也就是说,我们可以通过这个引用去获取到这个应用中的重要信息以及这个应用的通用操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342