聊聊 Context 的一些知识点

mApplication = application;
···
}

protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException(“Base context already set”);
}
mBase = base;
}

public final Application getApplication() {
return mApplication;
}

}

所以说,Activity 包含的 mBase 和 Application 都是在启动过程中得到的,Activity 在被实例化后,其 attach 方法就会被调用从而初始化这两个成员变量。Activity 直接继承于 ContextThemeWrapper,ContextThemeWrapper 又直接继承于 ContextWrapper,Activity 拿到的 ContextImpl 就用来初始化声明在 ContextWrapper 中的 mBase,之后 Activity 就可以使用 Context 中的各个方法了

二、Service

Service 的 Context 创建过程与 Activity 类似,主要看 ActivityThread 的 handleCreateService 方法,该方法就用于创建 Service 实例并回调其 onCreate 方法

@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
···
Service service = null;
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
Application app = packageInfo.makeApplication(false, mInstrumentation);

···

service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
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);
    }
    }
    }

在拿到 ContextImpl 与 Application 实例后,Service 的 attach 方法也完成了自身 mBase 和 mApplication 两个成员变量的初始化,整个过程和 Activity 十分类似

@UnsupportedAppUsage
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
···
mApplication = application;
···
}

三、BroadcastReceiver

BroadcastReceiver 的 Context 创建过程主要看 ActivityThread 的 handleReceiver 方法,该方法就用于创建 BroadcastReceiver 实例并回调其 onReceive 方法。由于系统限制了 BroadcastReceiver 不能用于注册广播和绑定服务,所以其 onReceive 方法传入的 Context 对象实际上属于 ContextWrapper 的子类 ReceiverRestrictedContext

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private void handleReceiver(ReceiverData data) {
···
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
app = packageInfo.makeApplication(false, mInstrumentation);
context = (ContextImpl) app.getBaseContext();
···
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
···
}
try {
···
//传递的是 ReceiverRestrictedContext
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
}
···
}

ReceiverRestrictedContext 重载了registerReceiverbindService 等方法,当被调用时会直接抛出异常,从而限制了 BroadcastReceiver 的相应功能

class ReceiverRestrictedContext extends ContextWrapper {

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
if (receiver == null) {
// Allow retrieving current sticky broadcast; this is safe since we
// aren’t actually registering a receiver.
return super.registerReceiver(null, filter, broadcastPermission, scheduler);
} else {
throw new ReceiverCallNotAllowedException(
“BroadcastReceiver components are not allowed to register to receive intents”);
}
}

@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException(
“BroadcastReceiver components are not allowed to bind to services”);
}

···
}

四、ContentProvider

ContentProvider 并不是 Context 的子类,但由于其属于四大组件之一,这里就一起讲下吧

在应用启动时系统会自动初始化 ContentProvider 并传入 Context 对象,因此很多三方开源库都选择通过 ContentProvider 来拿到 Context 对象并初始化自身,这样对于引用开源库的一方来说侵入性会更低

ContentProvider 的 Context 创建过程主要看 ActivityThread 的 installProvider 方法,该方法就用于创建 ContentProvider 实例。该方法在拿到 ContextImpl 实例后,就会再通过反射得到 ContentProvider 实例,然后再调用 ContentProvider 的 attachInfo 方法

@UnsupportedAppUsage
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) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": "

  • info.name);
    }
    Context c = null;
    //得到 ContextImpl 对象
    c = context.createPackageContext(ai.packageName,
    Context.CONTEXT_INCLUDE_CODE);
    ···
    try {
    ···
    //通过反射实例化 ContentProvider
    localProvider = packageInfo.getAppFactory()
    .instantiateProvider(cl, info.name);
    ···
    //传入 Context 对象
    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;
    }
    } else {
    provider = holder.provider;
    if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
  • info.name);
    }
    ···
    return retHolder;
    }

ContentProvider 的 attachInfo 方法最终就会初始化自身的 mContext 变量,然后再回调自身的 onCreate() 方法,从而完成自身的初始化

@UnsupportedAppUsage
private Context mContext = null;

public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
···
if (mContext == null) {
mContext = context;
···
ContentProvider.this.onCreate();
}
}

五、Application

回顾以上代码,可以看到 Activity、Service、BroadcastReceiver 三者都是通过 LoadedApk.makeApplication 方法拿到 Application 实例的,再来看下 Application Context 的创建流程

makeApplication 方法的流程可以分为四步:

  • 第一步,通过 createAppContext 方法创建 ContextImpl 实例,得到 appContext
  • 第二步,通过反射实例化 Application 并回调其 attach(Context) 方法,传入的 Context 参数即 appContext,从而初始化 Application 的 mBase 变量
  • 第三步,将得到的 Application 保存为全局变量 mApplication
  • 第四步,回调 Application 的 onCreate 方法

@UnsupportedAppUsage
private Application mApplication;

@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “makeApplication”);

Application app = null;

···
try {
···
//第一步
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
···
//第二步
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
···
}
mActivityThread.mAllApplications.add(app);
//第三步
mApplication = app;

if (instrumentation != null) {
try {
//第四步
instrumentation.callApplicationOnCreate(app);
} 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()

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BATJ 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-z6UUxlsV-1715129519797)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

[外链图片转存中…(img-6ISQzaTA-1715129519798)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值