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

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);
    }
    ···
    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) {
···

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值