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 重载了registerReceiver
和 bindService
等方法,当被调用时会直接抛出异常,从而限制了 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学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!