聊聊 Context 的一些知识点(2)

上文讲了,ContextWrapper 内部包含一个 ContextImpl 类型实例的成员变量mBase,因此 Activity 也同样包含。Activity 的mBase的初始化时机主要看 ActivityThread 的 performLaunchActivity 方法,该方法就用于在启动 Activity 时构建 Activity 实例。此外,我们知道 Activity 包含一个 getApplication()方法用于获取 Application 实例,那么在实例化 Activity 的时候需要一起把 ContextImpl 和 Application 传给 Activity

performLaunchActivity方法步骤大体上可以分为四步:

  • 第一步,通过 createBaseContextForActivity 方法创建 ContextImpl 实例,得到 appContext
  • 第二步,通过反射实例化 Activity,得到 activity
  • 第三步,拿到 Application 实例,即 app
  • 第四步,通过 attach 方法将 appContext 和 app 传给 activity,完成 mBase 和 Application 的初始化

/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
···
//第一步
ContextImpl appContext = createBaseContextForActivity®;
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//第二步
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
···
} catch (Exception e) {
···
}

try {
//第三步
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
···
if (activity != null) {
···
appContext.setOuterContext(activity);
//第四步
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,
r.assistToken);

···
}
···
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component

  • ": " + e.toString(), e);
    }
    }
    return activity;
    }

Activity 的 attach方法又会向 mApplicationmBase 两个成员变量赋值。以下属于伪代码,mBaseattachBaseContext 其实是声明在父类 ContextWrapper 中的,读者意会即可

public class Activity extends ContextThemeWrapper {

@UnsupportedAppUsage
Context mBase;

private Application mApplication;

@UnsupportedAppUsage
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, IBinder assistToken) {
attachBaseContext(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);
    }

最后

希望大家能有一个好心态,想进什么样的公司要想清楚,并不一定是大公司,我选的也不是特大厂。当然如果你不知道选或是没有规划,那就选大公司!希望我们能先选好想去的公司再投或内推,而不是有一个公司要我我就去!还有就是不要害怕,也不要有压力,平常心对待就行,但准备要充足。最后希望大家都能拿到一份满意的 offer !如果目前有一份工作也请好好珍惜好好努力,找工作其实挺累挺辛苦的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-9K5Wfe0I-1715359409851)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值