关于 Context ,打从接触 Android 开始,就看到了相关文章,自己的笔记里也做了相关的记录。觉得网上关于解析 Context 的优秀的博客已经很多了。自己再写好像也没有必要了。后来想开了,就当做对自己笔记的整理和再学习。其实主要是整合,囧。况且,看到的人也屈指可数。

Context,意为上下文。弄懂 Context 对于 Android 开发者还是比较重要的。首先,我们看下 Context 的主要继承结构。这里明确一点,是主要。

Context继承结构.jpg

Context 主要应用了装饰模式。ContextWrapper 主要是 Context 的封装类,ContextImpl 则是 Context 的实现类。我们熟悉的 Activity、Service 和 Application 都是 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 是一个抽象类。我们通过它可以访问当前包的资源(getResources、getAssets)和启动其他组件(Activity、Service、Broadcast)。

接下来我们来看 ContextImpl 类,这里只截取其中关于 startActivity 的实现。总是 ContentImpl 是 Context 的实现类。

/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */class ContextImpl extends Context {      @Override      public void startActivity(Intent intent, Bundle options) {          warnIfCallingFromSystemProcess();          if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {              throw new AndroidRuntimeException(                "Calling startActivity() from outside of an Activity "                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."                + " Is this really what you want?");       }      mMainThread.getInstrumentation().execStartActivity(        getOuterContext(), mMainThread.getApplicationThread(), null,        (Activity)null, intent, -1, options);      }}

我们来看 ContextWrapper 类,Content 维护了一个 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;    }    @Override    public AssetManager getAssets() {        return mBase.getAssets();    }    @Override    public Resources getResources()    {        return mBase.getResources();    }}

Activity、Service 和 Application 都是Context 的具体装饰类。那为什么 Activity 不直接继承 ContextWrapper 类,而是继承于 ContextThemeWrapper 这个好像多余的类呢?其实不然,我们来看下 ContentThemeWrapper 的源码注释:

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

原来这个类的目的是让我们可以去修改或者说替换 Cotnext 的主题Theme。即android:theme 属性指定的。

Context 数量

关于一个应用中到底有多少个Context其实是显而易见的了。Context 一共有 Application、Activity 和 Service 三种类型,因此一个应用中 Context 数量为:

Context 数量 = Activity 数量 + Service 数量 + 1(Applcation)

Context 实例化过程的源码分析

Activity 对象中 Context 的实例化。

通过 startActivity 启动一个 Activity 进过一系列的调用方法之后会来到 ActivityThread(即主线程)的 performLaunchActivity()方法来创建一个 Activity 实例,然后回调 Activity 的 onCreate()等方法。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {    ......        if (activity != null) {            //创建一个Context对象            Context appContext = createBaseContextForActivity(r, activity);            ......            //将上面创建的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);            ......        }    ......    return activity;}

可以看到,通过 createBaseContextForActivity(r,activity)
创建一个 Context 对象并在 attach 方法关联它。

我们再来看下 createBaseContextForActivity 方法。

private Context createBaseContextForActivity(ActivityClientRecord r,        final Activity activity) {    //实质就是new一个ContextImpl对象,调运ContextImpl的有参构造初始化一些参数        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);    //特别特别留意这里!!!    //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Activity对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Activity)。    appContext.setOuterContext(activity);    //创建返回值并且赋值    Context baseContext = appContext;    ......    //返回ContextImpl对象    return baseContext;}

总结:Activity 内部持有一个 ContextImpl 引用(ContextWrapper 的成员 mBase),进而 执行 Context(ContextImpl 继承于 Context) 中的方法。

Service 对象中 Context 的实例化。

通过 startService 或者 bindService 方法创建一个新 Service 时会回调 ActivityThread 类的 handleCreateService()方法完成相关数据操作。

private void handleCreateService(CreateServiceData data) {    ......    //类似上面Activity的创建,这里创建service对象实例    Service service = null;    try {        java.lang.ClassLoader cl = packageInfo.getClassLoader();        service = (Service) cl.loadClass(data.info.name).newInstance();    } catch (Exception e) {        ......    }    try {        ......        //不做过多解释,创建一个Context对象        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);        //特别特别留意这里!!!        //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Service对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Service)。        context.setOuterContext(service);        Application app = packageInfo.makeApplication(false, mInstrumentation);        //将上面创建的context传入到service的attach方法        service.attach(context, this, data.info.name, data.token, app,                ActivityManagerNative.getDefault());        service.onCreate();        ......    } catch (Exception e) {        ......    }}

Application对象中 Context 的实例化。

其实,Application 对象中 Context 对象的实例化 和 Activity 、Service 中的基本一致。ContentImpl 的创建在 LoadedApk 类 的 makeApplication 方法实现。

public Application makeApplication(boolean forceDefaultAppClass,        Instrumentation instrumentation) {    //只有新创建的APP才会走if代码块之后的剩余逻辑    if (mApplication != null) {        return mApplication;    }    //即将创建的Application对象    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();        }        //不做过多解释,创建一个Context对象        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);        //将Context传入Instrumentation类的newApplication方法        app = mActivityThread.mInstrumentation.newApplication(                cl, appClass, appContext);        //特别特别留意这里!!!        //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Application对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Application)。        appContext.setOuterContext(app);    } catch (Exception e) {        ......    }    ......    return app;}

应用程序 App 各种 Context 访问资源的唯一性

可以发现,Application、Activity 和 Service 都有自己 Context 的实例,那么平常我们通过 context.getResources() 得到的资源是不是同一份呢?

class ContextImpl extends Context {    ......    private final ResourcesManager mResourcesManager;    private final Resources mResources;    ......    @Override    public Resources getResources() {        return mResources;    }    ......}

context.getResources 方法获得的 Resources 对象就是上面 ContextImpl 的成员变量 mResources。mResource 的赋值操作如下:

private ContextImpl(ContextImpl container, ActivityThread mainThread,        LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,        Display display, Configuration overrideConfiguration) {    ......    //单例模式获取ResourcesManager对象    mResourcesManager = ResourcesManager.getInstance();    ......    //packageInfo对于一个APP来说只有一个,所以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);        }    }    //把resources赋值给mResources    mResources = resources;    ......}

由此可以看出在设备等其他因素不变的情况下我们通过 Context 实例得到的 Resources 是同一套资源。这里要注意的是只有在设备等其他因素不变的情况下,这句话才是使用的。因为 res 下可能存在多个适配不同设备、不同分辨率、不同系统版本的目录,不同设备在访问同一个应用的时候可以不同,有可能是 hdpi 的 或者 xxhdpi 的。而且如果为横竖屏状态下提供了不同的资源,-land、-port,处在横屏状态下的 ContextImpl 和处在竖屏状态下的 ContextImpl 访问的资源也不是同一个资源对象。

getApplication 和 getApplicationContext 的区别

首先我们看 getApplication 方法,这个方法是 Activity 和 Service 中才有的。Application 和 Context 都没有此方法。

public class Activity extends ContextThemeWrapper    implements LayoutInflater.Factory2,    Window.Callback, KeyEvent.Callback,    OnCreateContextMenuListener, ComponentCallbacks2,    Window.OnWindowDismissedCallback {    ......    public final Application getApplication() {        return mApplication;    }    ......}public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {    ......    public final Application getApplication() {        return mApplication;   }    ......}

Activity 和 Service 提供了getApplication方法,而且返回类型都是 Application。这个 Application 是在 ActivityThread 中各自实例化时获取的 make Application 方法返回值。所以不同的 Activity 和 Service 返回的Application均为同一个全局对象。

接着我们来看 getApplicationContext 方法,

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

可以看到,getApplicationContext 方法 是Context 方法,而且返回值是 Context 类型,返回对象和上面通过 Service 或者 Activity 的 getApplication 返回的是一个对象。

因此 getApplication 方法 和 getApplicationContext 方法只是返回值类型不同,一个返回的是 Application ,另一个是 Context。还有就是依附的对象不同而已。

参考资料

[1] 工匠若水.Android应用Context详解及源码解析
[2] singwhatiwannaAndroid源码分析-全面理解Context

更多相关文章

  1. android 开发零起步学习笔记(十一):界面切换+几种常用界面切换效果
  2. Android带进度条的文件上传,使用AsyncTask异步任务
  3. Android——组件之Service
  4. android 学习方法
  5. Android(安卓)Native Crash崩溃及错误原因分析二-实战解决
  6. Android开发中TextView文本过长滚动显示实现方法分析
  7. 浅谈:Android(安卓)TextView的append方法与滚动条同时使用
  8. Android(安卓)>> 14. LiveData
  9. 【Android应用实例之二】跟随手指的小球——自定义View应用

随机推荐

  1. Android(安卓)VelocityTracker的使用
  2. Android Studio3.4.2配置NDK
  3. Android系统源代码下载
  4. Android Build 系统
  5. Android9.0 添加APP后台防杀机制
  6. Android 和 JS 交互时调用不成功解决方法
  7. Android在Activity中获得控件宽高和截屏
  8. android退出登陆后,清空之前所有的activit
  9. android 自动获取时间,网络提供的值
  10. Android动画效果translate、scale、alpha