Android Context总结


Context是什么?Android应用的上下文,既然是上下文,就说明其所拥有的功能,是要贯穿整个app的生命周期的,比如说APK资源,系统服务等

在Android中,对于系统创建的对象,咱们能从其身上拿到Context的主要有5个,四大组件和Application对象

  • Activity,Service,Application都派生自ContextWrapper,说明其本身就是一个代理Context,宿主Context会在组件创建后调用类似attach传入
  • BroadcastReceiver在onReceive时会传入context
  • ContextProvider会在创建后调用attachInfo传入context

那每个组件使用的Context又有什么区别呢?很简单,直接看组件创建时,这些Context是哪里来的就清楚了

Application

直接看LoaedApk.makeApplication中创建application的代码

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
        cl, appClass, appContext);
appContext.setOuterContext(app);

ContextImpl.createAppContext:

 static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
       if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
       return new ContextImpl(null, mainThread,
               packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
   }

然后看mInstrumentation.newApplication:

static public Application newApplication(Class<?> clazz, Context context)
           throws InstantiationException, IllegalAccessException, 
           ClassNotFoundException {
       Application app = (Application)clazz.newInstance();
       app.attach(context);
       return app;
   }

看到了,application的宿主context实际上就是其在被创建时对应创建的ContextImpl对象

Activity

 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        int displayId = Display.DEFAULT_DISPLAY;
        try {
            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        // 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.
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if (pkgName != null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if (id != Display.DEFAULT_DISPLAY) {
                    Display display =
                            dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
                    baseContext = appContext.createDisplayContext(display);
                    break;
                }
            }
        }
        return baseContext;
    }

接着看ContextImpl.createActivityContext

   static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
                null, overrideConfiguration, displayId);
    }

Service

在ActivityThread.handleCreateService中:

 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,
    ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);

代码中很明显,service被创建的时,同同时创建一个app context传入,作为contextwrapper的宿主对象

ContentProvider

直接看ActivityThread中的installProvider的代码

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;
    }
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);

传入的c是installProvider传入的,从代码追溯可以发现,这个c就是mInitialApplication,最后看mInitialApplication是怎么被创建的:

   ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
   mInitialApplication = context.mPackageInfo.makeApplication(true, null);
   mInitialApplication.onCreate();

看到没,它就是contentprovider所属的applicaiton对象本身

BroadcastReceiver

还是看ActivityThread中handleReceiver部分代码

ContextImpl context = (ContextImpl)app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
    data.intent);

看到没,其拿的是application的宿主context,然后从context中拿到ReceiverRestrictedContext,这个ReceiverRestrictedContext其实也没什么,同样派生自ContextWrapper,其宿主是Application,不同的时,ReceiverRestrictedContext做了registerreceiver限制

ApplicationContext

还有就是我们经常使用的Context.getApplicationContext了,它其实就是Application本身

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

总结

  • Application,Activity,Service都是派生自ContextWrapper,他们在创建时都会同时创建对应的ContextImpl,它才是真实被挂载Context宿主
  • context提供了贯穿整个app相关资源的获取,那能整个App只使用一个ContextImpl宿主吗?答案是不行的,因为ContextImpl做不到全局独立,比如其内部的Resource查找资源,根据Android的特性,会随着Activity的显示属性的变化,需要变更查找维度的
  • ContentProvider attach的context其实就是application
  • applicationContext就是application本身
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值