Android Context简介

前言
Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像难以说清楚。从字面意思,Context的意思是“上下文”,或者也可以叫做环境、场景等,尽管如此,还是有点抽象。从类的继承来说,Context作为一个抽象的基类,它的实现子类有三种:Application、Activity和Service(估计这么说,暂时不管ContextWrapper等类),那么这三种有没有区别呢?为什么通过任意的Context访问资源都得到的是同一套资源呢?getApplication和getApplicationContext有什么区别呢?应用中到底有多少个Context呢?本文将围绕这些问题一一展开,所用源码版本为Android4.4。

什么是Context
Context是一个抽象基类,我们通过它访问当前包的资源(getResources、getAssets)和启动其他组件(Activity、Service、Broadcast)以及得到各种服务(getSystemService),当然,通过Context能得到的不仅仅只有上述这些内容。对Context的理解可以来说:Context提供了一个应用的运行环境,在Context的大环境里,应用才可以访问资源,才能完成和其他组件、服务的交互,Context定义了一套基本的功能接口,我们可以理解为一套规范,而Activity和Service是实现这套规范的子类,这么说也许并不准确,因为这套规范实际是被ContextImpl类统一实现的,Activity和Service只是继承并有选择性地重写了某些规范的实现。

Context家族关系
这里写图片描述

Context对资源的访问
很明确,不同的Context得到的都是同一份资源。这是很好理解的,请看下面的分析

得到资源的方式为context.getResources,而真正的实现位于ContextImpl中的getResources方法,在ContextImpl中有一个成员 private Resources mResources,它就是getResources方法返回的结果,mResources的赋值代码为:

mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);

getApplication和getApplicationContext的区别

getApplication返回结果为Application,且不同的Activity和Service返回的Application均为同一个全局对象,在ActivityThread内部有一个列表专门用于维护所有应用的application

final ArrayList<Application> mAllApplications  = new ArrayList<Application>();

getApplicationContext返回的也是Application对象,只不过返回类型为Context,看看它的实现

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

上面代码中mPackageInfo是包含当前应用的包信息、比如包名、应用的安装目录等,原则上来说,作为第三方应用,包信息mPackageInfo不可能为空,在这种情况下,getApplicationContext返回的对象和getApplication是同一个。但是对于系统应用,包信息有可能为空,具体就不深入研究了。从这种角度来说,对于第三方应用,一个应用只存在一个Application对象,且通过getApplication和getApplicationContext得到的是同一个对象,两者的区别仅仅是返回类型不同。

何时创建Context
应用程序在以下几种情况下创建Context实例:
1) 创建Application 对象时, 而且整个App共一个Application对象
2) 创建Service对象时
3) 创建Activity对象时

因此应用程序App共有的Context数目公式为:
总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context实例)

ActivityThread消息处理函数与本节相关的内容如下:

public void handleMessage(Message msg) {  
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));  
    switch (msg.what) {  
        case LAUNCH_ACTIVITY: { // 创建Activity对象  
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;  

            r.packageInfo = getPackageInfoNoCheck(  
                    r.activityInfo.applicationInfo, r.compatInfo);  
            handleLaunchActivity(r, null);  
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
        } break;  

        case BIND_APPLICATION: // 创建Application对象  
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");  
            AppBindData data = (AppBindData)msg.obj;  
            handleBindApplication(data);  
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
            break;  

        case CREATE_SERVICE: // 创建Service对象  
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");  
            handleCreateService((CreateServiceData)msg.obj);  
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
            break;  

        case BIND_SERVICE:  // Bind Service对象  
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");  
            handleBindService((BindServiceData)msg.obj);  
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
            break;  
    }  
}  

创建Application对象时创建Context实例
每个应用程序在第一次启动时,都会首先创建一个Application对象。从startActivity流程可知,创建Application的时机在handleBindApplication()方法中,该函数位于 ActivityThread.java类中 ,相关代码如下:

// ActivityThread.java  
private void handleBindApplication(AppBindData data) {   
   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);  
         mInitialApplication = app;  
         ...  
     } finally {  
         StrictMode.setThreadPolicy(savedPolicy);  
     }  
}  

// LoadedApk.java  
public Application makeApplication(boolean forceDefaultAppClass,  
         Instrumentation instrumentation) {  
     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();  
         ContextImpl appContext = new ContextImpl(); // 创建ContextImpl实例  
         appContext.init(this, null, mActivityThread);  
         app = mActivityThread.mInstrumentation.newApplication(  
                 cl, appClass, appContext);  
         appContext.setOuterContext(app); // 将Application实例传递给Context实例  
     } catch (Exception e) {  
         ...  
     }  
     mActivityThread.mAllApplications.add(app);  
     mApplication = app;  

     return app;  
 }  

创建Activity对象时创建Context实例
通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调onCreate(),onStart()方法等,函数都位于 ActivityThread.java类 ,相关代码如下:

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
    ...  
    Activity a = performLaunchActivity(r, customIntent); // 到下一步  

    if (a != null) {  
        r.createdConfig = new Configuration(mConfiguration);  
        Bundle oldState = r.state;  
        handleResumeActivity(r.token, false, r.isForward,  
                !r.activity.mFinished && !r.startsNotResumed);  
        ...  
    }  
    ...  
 }  

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
    ...      
    Activity activity = null;  
    try {  
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
        activity = mInstrumentation.newActivity(  
                cl, component.getClassName(), r.intent);  
        StrictMode.incrementExpectedActivityCount(activity.getClass());  
        r.intent.setExtrasClassLoader(cl);  
        if (r.state != null) {  
            r.state.setClassLoader(cl);  
        }  
    } catch (Exception e) {  
        ...  
    }  

    try {  
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);  

        if (activity != null) {  
            Context appContext = createBaseContextForActivity(r, activity); // 创建Context  
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  
            Configuration config = new Configuration(mCompatConfiguration);  
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "  
                    + r.activityInfo.name + " with config " + config);  
            activity.attach(appContext, this, getInstrumentation(), r.token,  
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,  
                    r.embeddedID, r.lastNonConfigurationInstances, config);  

            if (customIntent != null) {  
                activity.mIntent = customIntent;  
            }  
            r.lastNonConfigurationInstances = null;  
            activity.mStartedActivity = false;  
            int theme = r.activityInfo.getThemeResource();  
            if (theme != 0) {  
                activity.setTheme(theme);  
            }  


        mActivities.put(r.token, r);  

    } catch (SuperNotCalledException e) {  
        ...  

    } catch (Exception e) {  
        ...  
    }  

    return activity;  
}  
private Context createBaseContextForActivity(ActivityClientRecord r,  
        final Activity activity) {  
    ContextImpl appContext = new ContextImpl();  // 创建ContextImpl实例  
    appContext.init(r.packageInfo, r.token, this);  
    appContext.setOuterContext(activity);  

    // For debugging purposes, if the activity's package name contains the value of  
    // the "debug.use-second-display" system property as a substring, then show  
    // its content on a secondary display if there is one.  
    Context baseContext = appContext;  
    String pkgName = SystemProperties.get("debug.second-display.pkg");  
    if (pkgName != null && !pkgName.isEmpty()  
            && r.packageInfo.mPackageName.contains(pkgName)) {  
        DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();  
        for (int displayId : dm.getDisplayIds()) {  
            if (displayId != Display.DEFAULT_DISPLAY) {  
                Display display = dm.getRealDisplay(displayId);  
                baseContext = appContext.createDisplayContext(display);  
                break;  
            }  
        }  
    }  
    return baseContext;  
}  

创建Service对象时创建Context实例
通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:

private void handleCreateService(CreateServiceData data) {  
    // If we are getting ready to gc after going to the background, well  
    // we are back active so skip it.  
    unscheduleGcIdler();  

    LoadedApk packageInfo = getPackageInfoNoCheck(  
            data.info.applicationInfo, data.compatInfo);  
    Service service = null;  
    try {  
        java.lang.ClassLoader cl = packageInfo.getClassLoader();  
        service = (Service) cl.loadClass(data.info.name).newInstance();  
    } catch (Exception e) {  
        if (!mInstrumentation.onException(service, e)) {  
            throw new RuntimeException(  
                "Unable to instantiate service " + data.info.name  
                + ": " + e.toString(), e);  
        }  
    }  

    try {  
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);  

        ContextImpl context = new ContextImpl(); // 创建ContextImpl实例  
        context.init(packageInfo, null, this);  

        Application app = packageInfo.makeApplication(false, mInstrumentation);  
        context.setOuterContext(service);  
        service.attach(context, this, data.info.name, data.token, app,  
                ActivityManagerNative.getDefault());  
        service.onCreate();  
        mServices.put(data.token, service);  
        try {  
            ActivityManagerNative.getDefault().serviceDoneExecuting(  
                    data.token, 0, 0, 0);  
        } catch (RemoteException e) {  
            // nothing to do.  
        }  
    } catch (Exception e) {  
        if (!mInstrumentation.onException(service, e)) {  
            throw new RuntimeException(  
                "Unable to create service " + data.info.name  
                + ": " + e.toString(), e);  
        }  
    }  
}  

小结
通过对ContextImp的分析可知,其方法的大多数操作都是直接调用其属性mPackageInfo(该属性类型为PackageInfo)的相关方法而来。这说明ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的所有ContextImpl实例,都对应同一个packageInfo对象。

转载:http://blog.csdn.net/myarrow/article/details/14121757
http://www.cnblogs.com/android100/p/Android-Context.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值