目录

  • 简介
  • Context类图
  • 源码中Context创建
    • Application
    • Activity
    • Service
    • BroadcastReceiver
    • ContentProvider
  • Context使用
    • 普通Context
    • de/ce

1. 简介

在Android开发中少不了要跟Context打交道,比如启动activity,启动service等。但是又很难表述这个context到底是个什么东西,从字面意思来看应该叫做“上下文”,确实在开发过程中这个context也给人感觉无处不在,但又非常抽象,难以把握实质。其实产生这样感觉的原因是我们大部分的开发都在跟组件打交道,组件与Context又有着密不可分的关系。

2. Context类图

Android Context原理分析_第1张图片
从上面这个图大致可以看出来一些信息:

  1. Context本身是一个抽象类,定义了很多common的抽象方法
  2. ContextImpl和ContextWrapper是具体的实现类
  3. ContextWrapper中持有ContextImpl类型的mBase对象
  4. Application和Service都是ContextWrapper的子类
  5. BraodcastReceiver不是ContextWrapper的子类但是有一个ReceiverRestrictedContext
  6. 对于Activity而言,由于涉及到theme和window,其父类为ContextThemeWrapper,而ContextThemeWrapper的父类也是ContextWrapper
  7. 日常熟悉的startActivity/startService等对于组件常用的方法在抽像类中就已经定义,在ContextWrapper中实现
  8. 用到了装饰模式,ContextWrapper中通过ComtextImpl对象实现真正的操作

3. 源码中Context创建

3.1 Application

每个app的进程在创建之后,在ActivityThread的handleBindApplication中会去通过LoadedApk创建Application。
LoadedApk:可以认为是应用的apk在进程中对应的对象,每个apk在每个进程中只创建一次

    public Application makeApplication(boolean forceDefaultAppClass,            Instrumentation instrumentation) {        ...        String appClass = mApplicationInfo.className;        if (forceDefaultAppClass || (appClass == null)) {            appClass = "android.app.Application";        }        try {            java.lang.ClassLoader cl = getClassLoader();            if (!mPackageName.equals("android")) {                initializeJavaContextClassLoader();            }            // 创建application的contextImpl对象            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);            // 通过反射创建Application对象            app = mActivityThread.mInstrumentation.newApplication(                    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;        ...        return app;    }        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    /* package */ final void attach(Context context) {        attachBaseContext(context);        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;    }    // ContextWrapper.attachBaseContext    protected void attachBaseContext(Context base) {        if (mBase != null) {            throw new IllegalStateException("Base context already set");        }        mBase = base;    }

这里可以看到

  1. 先是通过ContextImpl的静态方法创建了ContextImpl对象
  2. 随后通过反射创建了真正的application对象
  3. 在newApplication中会逐级调用,到application的attach方法,并在其中调用attachBaseContext,在其中将前面创建的ContextImpl赋给了Application对象中的mBase
  4. 前面已经说过Application是ContextWrapper的子类,默认含有mBase成员,而这个成员正是在此处赋值
  5. Application的attach方法以及attachBaseContext的执行是在Application的onCreate之前的(也在provider的onCreate之前)
  6. 注意这个appContext.setOuterContext(app);把application设置为刚创建的ContextImpl的OuterContext
    这个mBase的作用后面会进一步说明,其实就是所有需要context操作的地方真正实现其实就是用的mBase

3.2 Activity

Activity的创建流程比较复杂,这里不贴过多源码,重点介绍与创建以及与context的关系,在handleRelaunchActivity中:
handleRelaunchActivity -> 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);        }        ComponentName component = r.intent.getComponent();        if (component == null) {            component = r.intent.resolveActivity(                mInitialApplication.getPackageManager());            r.intent.setComponent(component);        }        if (r.activityInfo.targetActivity != null) {            component = new ComponentName(r.activityInfo.packageName,                    r.activityInfo.targetActivity);        }        ContextImpl appContext = createBaseContextForActivity(r);        Activity activity = null;        try {            // 这里看起来就比较熟悉,因为跟前面application的创建基本是类似的            java.lang.ClassLoader cl = appContext.getClassLoader();            activity = mInstrumentation.newActivity(                    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);            }        }        ...        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());                Configuration config = new Configuration(mCompatConfiguration);                if (r.overrideConfig != null) {                    config.updateFrom(r.overrideConfig);                }                Window window = null;                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {                    window = r.mPendingRemoveWindow;                    r.mPendingRemoveWindow = null;                    r.mPendingRemoveWindowManager = null;                }                appContext.setOuterContext(activity);                // 创建之后执行application的attch方法                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;    }        public Activity newActivity(ClassLoader cl, String className,            Intent intent)            throws InstantiationException, IllegalAccessException,            ClassNotFoundException {        return (Activity)cl.loadClass(className).newInstance();    }    // 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);        ...    }    // Activity.attachBaseContext    protected void attachBaseContext(Context newBase) {        super.attachBaseContext(newBase);        newBase.setAutofillClient(this);    }    // ContextThemeWrapper.attachBaseContext    protected void attachBaseContext(Context newBase) {        super.attachBaseContext(newBase);    }    // ContextWrapper.attachBaseContext    protected void attachBaseContext(Context base) {        if (mBase != null) {            throw new IllegalStateException("Base context already set");        }        mBase = base;    }

整体的流程如下:

  1. 创建ContextImpl
  2. 反射创建activity对象
  3. 执行activity.attch
  4. 执行activity.attachBaseContext
  5. 执行ContextThemeWrapper.attachBaseContext
  6. 执行ContextWrapper.attachBaseContext
    在ContextWrapper.attachBaseContext中,将前面创建的ContextImpl赋给了mBase
    整体上这个流程跟前面application的创建基本上是完全类似的,先是对象的创建,然后在attach中赋值mBase

3.3 Service

service的创建流程相对简单,在handleCreateService中:

    private void handleCreateService(CreateServiceData data) {        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 {            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);            context.setOuterContext(service);            Application app = packageInfo.makeApplication(false, mInstrumentation);            service.attach(context, this, data.info.name, data.token, app,                    ActivityManager.getService());            service.onCreate();            mServices.put(data.token, service);            try {                ActivityManager.getService().serviceDoneExecuting(                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);            } catch (RemoteException e) {                throw e.rethrowFromSystemServer();            }        } catch (Exception e) {            if (!mInstrumentation.onException(service, e)) {                throw new RuntimeException(                    "Unable to create service " + data.info.name                    + ": " + e.toString(), e);            }        }    }        public final void attach(            Context context,            ActivityThread thread, String className, IBinder token,            Application application, Object activityManager) {        attachBaseContext(context);        mThread = thread;        mClassName = className;        mToken = token;        mApplication = application;        mActivityManager = (IActivityManager)activityManager;        mStartCompatibility = getApplicationInfo().targetSdkVersion                < Build.VERSION_CODES.ECLAIR;    }

跟前面一样,创建了service使用的ContextImpl,创建service对象,调用service的attach,最终完成赋值mBase,这里创建完之后就直接会调了service的onCreate。

3.4 BroadcastReceiver

其实从前面的类图就能看出来,其实broadcast和context并没有直接关系,确实在BroadcasReceiver中我们也不能直接就调用startActivity等方法,但是onReceive方法中却有一个Context对象,这个对象是怎么来的呢?

    private void handleReceiver(ReceiverData data) {        unscheduleGcIdler();        String component = data.intent.getComponent().getClassName();        LoadedApk packageInfo = getPackageInfoNoCheck(                data.info.applicationInfo, data.compatInfo);        IActivityManager mgr = ActivityManager.getService();        Application app;        BroadcastReceiver receiver;        ContextImpl context;        try {            app = packageInfo.makeApplication(false, mInstrumentation);            context = (ContextImpl) app.getBaseContext();            if (data.info.splitName != null) {                context = (ContextImpl) context.createContextForSplit(data.info.splitName);            }            java.lang.ClassLoader cl = context.getClassLoader();            data.intent.setExtrasClassLoader(cl);            data.intent.prepareToEnterProcess();            data.setExtrasClassLoader(cl);            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();        } catch (Exception e) {            if (DEBUG_BROADCAST) Slog.i(TAG,                    "Finishing failed broadcast to " + data.intent.getComponent());            data.sendFinished(mgr);            throw new RuntimeException(                "Unable to instantiate receiver " + component                + ": " + e.toString(), e);        }        try {            sCurrentBroadcastIntent.set(data.intent);            receiver.setPendingResult(data);            receiver.onReceive(context.getReceiverRestrictedContext(),                    data.intent);        } catch (Exception e) {            if (DEBUG_BROADCAST) Slog.i(TAG,                    "Finishing failed broadcast to " + data.intent.getComponent());            data.sendFinished(mgr);            if (!mInstrumentation.onException(receiver, e)) {                throw new RuntimeException(                    "Unable to start receiver " + component                    + ": " + e.toString(), e);            }        } finally {            sCurrentBroadcastIntent.set(null);        }        if (receiver.getPendingResult() != null) {            data.finish();        }    }        final Context getReceiverRestrictedContext() {        if (mReceiverRestrictedContext != null) {            return mReceiverRestrictedContext;        }        return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());    }

这里可以看到,参数中的context是从Application.getBaseContext得来之后,经过ReceiverRestrictedContext对getOuterContext()的对象转换来的。
前面创建application的时候有一步:setOuterContext,这里就用的正是前面set进去的application对象。
getBaseContext:

    public Context getBaseContext() {        return mBase;    }

getOuterContext:

    final void setOuterContext(Context context) {        mOuterContext = context;    }    final Context getOuterContext() {        return mOuterContext;    }

也就是说,getBaseContext获取到的是application中的ContextImpl对象,getOuterContext获取到的是application对象,ReceiverRestrictedContext是通过application对象创建的。

3.5 ContentProvider

ContentProvider跟Context没有直接的在类的继承上的关系:

    private ContentProviderHolder installProvider(Context context,            ContentProviderHolder holder, ProviderInfo info,            boolean noisy, boolean noReleaseNeeded, boolean stable) {        ContentProvider localProvider = null;        IContentProvider provider;        if (holder == null || holder.provider == null) {            Context c = null;            ApplicationInfo ai = info.applicationInfo;            if (context.getPackageName().equals(ai.packageName)) {                c = context;            } else if (mInitialApplication != null &&                    mInitialApplication.getPackageName().equals(ai.packageName)) {                c = mInitialApplication;            } else {                try {                    c = context.createPackageContext(ai.packageName,                            Context.CONTEXT_INCLUDE_CODE);                } catch (PackageManager.NameNotFoundException e) {                    // Ignore                }            }            ...            try {                final java.lang.ClassLoader cl = c.getClassLoader();                localProvider = (ContentProvider)cl.                    loadClass(info.name).newInstance();                provider = localProvider.getIContentProvider();                if (provider == null) {                    Slog.e(TAG, "Failed to instantiate class " +                          info.name + " from sourceDir " +                          info.applicationInfo.sourceDir);                    return null;                }                localProvider.attachInfo(c, info);            } catch (java.lang.Exception e) {                if (!mInstrumentation.onException(null, e)) {                    throw new RuntimeException(                            "Unable to get provider " + info.name                            + ": " + e.toString(), e);                }                return null;            }        }        ...        return retHolder;    }

这里还是用到了Context,这个对象其实是在provider创建的时候从外部传进来了,一般是application对象,在handleBindApplication中创建了application之后,会使用application去installProvider。

4. Context使用

4.1 普通context

  1. 在activity/service等里面可以直接调用context的方法的原因现在已经一清二楚了,正是因为它们本身就是context对象,查看源码可以发现,所有common方法的实现基本都会在ContextWrapper中,而在ContextWrapper中调的是mBase的实现,也就是其实所有的实现都在ContextImpl中。
  2. 避免内存泄漏,这个其实很简单,因为activity是context对象,但是很多时候可能我们使用到activity这个context的时候,使用的地方的生命周期与activity的生命周期不一致,可能activity本应该已经被销毁了,但是由于我们的引用导致无法回收,这时候应该使用application的context。
  3. 是否所有场景都适用application的context?显然不是,通过前面的类图也能看出来,activity相比于application多了很多的东西,最主要是theme相关,在系统里面弹出dialog等时还是需要activity。
  4. getApplication与getApplicationContext区别?
    public Context getApplicationContext() {        return (mPackageInfo != null) ?                mPackageInfo.getApplication() : mMainThread.getApplication();    }

getApplication返回的是Application对象,getApplicationContext一般和getApplication返回的是同一个对象。但是getApplication并不是所有地方都可以使用,只有在activity/service/application中可以,receiver中要区分静态注册和动态注册,getApplicationContext。

4.2 de/ce

从Android N开始,引入了directBoot,也称为直接启动。
简而言之,就是系统分别提供了两个地方存储app的数据,一个是在用户解锁之后才能访问(凭据加密存储ce),另一个在“直接启动”模式下和用户解锁设备后可访问(设备加密存储de),默认情况下都在ce下。
如果app想要在解锁之前就启动,则需要在需要启动的组建中声明android:directBootAware="true"并且需要通过Context.createDeviceProtectedStorageContext() 创建另一个Context实例,调用之后会创建新的ContextImpl对象。

    public Context createDeviceProtectedStorageContext() {        final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)                | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,                flags, mClassLoader);    }    public Context createCredentialProtectedStorageContext() {        final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)                | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,                flags, mClassLoader);    }

更多可以参考:https://developer.android.com/training/articles/direct-boot

更多相关文章

  1. 让android定时关机的实现方法
  2. android Listview中button 和Listview可同时点击的方法
  3. android添加后门的一种方法
  4. Android ViewGroup中有关测量的方法
  5. android 中 unable to start service 错误解决方法
  6. Android之Servic的生命周期和调用方法

随机推荐

  1. 微信ANDROID客户端-会话速度提升70%的背
  2. View类的XML属性、相关方法及说明
  3. Android(安卓)仿淘宝选中商品不同尺寸的
  4. [Android]Android FTP Server
  5. android fragment ontouch 事件
  6. 一次偶然机遇找到一个不错的关于Android
  7. Android锁屏 DevicePolicyManager介绍
  8. [Android] 如何查看当前分支
  9. Android:interpolator用法
  10. Android Battery 架构