商城网站建设特点有哪些,网站开发工程师 课程大纲,网站建站wordpress,凡科平台盲审前言
前面已经讲了什么是context以及从源码角度分析context创建流程#xff08;上#xff09;。限于篇幅把四大组件中的广播和内容提供器的context获取流程放在了这篇文章。广播和内容提供器并不是context家族里的一员#xff0c;所以他们本身并不是context#xff0c;因而…前言
前面已经讲了什么是context以及从源码角度分析context创建流程上。限于篇幅把四大组件中的广播和内容提供器的context获取流程放在了这篇文章。广播和内容提供器并不是context家族里的一员所以他们本身并不是context因而他们的context肯定是直接或间接从Application、Activity或者Service获取。然后对context的设计进行了讨论从更高的角度看context能够帮助我们看到context的本质也能帮助我们更好地理解并使用context。 Broadcast的context获取流程
Broadcast和上面的组件不同他并不是继承自Context所以他的Context是需要通过Application、Activity或者Service来给予。我们一般使用广播的context是在接收器中如
class MyClass :BroadcastReceiver() {override fun onReceive(context: Context?, intent: Intent?) {TODO(use context)}
}那么onReceive的context对象是从哪里来的呢同样我们先看广播接收器的注册流程 同样详细的广播相关工作流程可以阅读Android广播Broadcast的注册与广播源码过程详解基于api29这篇文章了解。因为在创建Receiver的时候并没有传入context所以我们需要追踪他的注册流程看看在哪里获取了context。我们先看到ContextImpl的registerReceiver方法
ContextImpl.class(api29)
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {// 注意参数return registerReceiverInternal(receiver, getUserId(),filter, broadcastPermission, scheduler, getOuterContext(), 0);
}registerReceiver方法最终会来到这个重载方法我们可以注意到这里有个getOuterContext这个是什么还记得Activity的context创建过程吗这个方法获取的就是activity本身。我们继续看下去
ContextImpl.class(api29)
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context, int flags) {IIntentReceiver rd null;if (receiver ! null) {if (mPackageInfo ! null context ! null) {...rd mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);}...}...
}这里利用context创建了ReceiverDispatcher我们继续深入看
LoadedApk.class(api29)
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd null;...if (rd null) {rd new ReceiverDispatcher(r, context, handler,instrumentation, registered);...}...}
}ReceiverDispatcher.class(api29)
ReceiverDispatcher(..., Context context,...) {...mContext context;...
}这里确实把receiver和context创建了ReceiverDispatcher嗯?怎么没有给Receiver?其实这涉及到广播的内部设计结构。Receiver是没有跨进程通信能力的而广播需要AMS的调控所以必须有一个可以跟AMS沟通的对象这个对象是InnerReceiver而ReceiverDispatcher就是负责维护他们两个的联系如下图 而onReceive方法也是由ReceiverDispatcher回调的最后我们再看到回调onReceive的那部分代码
ReceiverDispatcher.java/Args.class;
public final Runnable getRunnable() {return () - {...;try {...;// 可以看到这里回调了receiver的方法这样整个接收广播的流程就走完了。receiver.onReceive(mContext, intent);}}
}Args是Receiver的内部类mContext就是在创建ReceiverDispatcher时传入的对象到这里我们就知道这个对象确实是Activity了。
但是不一定每个都是Activity。在源码中我们知道是通过getOuterContext来获取context如果是通过别的context注册广播那么对应的对象也就不同了只是我们一般都是在Activity中创建广播所以这个context一般是activity对象。
ContentProvider的context获取流程
ContextProvider我们用的就比较少了内容提供器主要是用于应用间内容共享的。虽然ContentProvider是由系统创建的但是他本身并不属于Context家族体系内所以他的context也是从其他获取的。老样子先看ContentProvider的创建流程 咦这不是Application创建的流程图吗是的ContentProvider是伴随着应用启动被创建的来看一张更加详细的流程图 我们把目光聚集到ContentProvider的创建上也就是installContentProviders方法。同样详细的ContentProvider工作流程可以访问Android中ContentProvider的启动与请求源码流程详解基于api29这篇文章。installContentProviders是在handleBindApplication中被调用的我们看到调用这个方法的地方
private void handleBindApplication(AppBindData data) {try {// 创建Applicationapp data.info.makeApplication(data.restrictedBackupMode, null);...if (!data.restrictedBackupMode) {if (!ArrayUtils.isEmpty(data.providers)) {// 安装ContentProviderinstallContentProviders(app, data.providers);}}
}可以看到这里传入了application对象我们继续看下去
private void installContentProviders(Context context, ListProviderInfo providers) {final ArrayListContentProviderHolder results new ArrayList();for (ProviderInfo cpi : providers) {...ContentProviderHolder cph installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);...}
...
}这里调用了installProvider继续往下看
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) {...// 这里c最终是由context构造的Context c null;ApplicationInfo ai info.applicationInfo;if (context.getPackageName().equals(ai.packageName)) {c context;}...try {// 创建ContentProviderfinal java.lang.ClassLoader cl c.getClassLoader();LoadedApk packageInfo peekPackageInfo(ai.packageName, true);...localProvider packageInfo.getAppFactory().instantiateProvider(cl, info.name);provider localProvider.getIContentProvider();...// 把context设置给ContentProviderlocalProvider.attachInfo(c, info);} ...} ...
}这里最重要的一行代码是localProvider.attachInfo(c, info);在这里把context设置给了ContentProvider我们再深入一点看看
ContentProvider.class(api29)
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;...}...
}这里确实把context赋值给了ContentProvider的内部变量mContext这样ContentProvider就可以使用Context了。而这个context正是一开始传进来的Application。
总结
Context承受的两大重要职责是身份权限、程序访问系统的接口。一个Java类如果没有context那么就是一个普通的Java类而当他获得context那么他就可以称之为一个组件了因为它获得了访问系统的权限他不再是一个普通的身份是属于android“公民”了。而“公民”并不是无法无天系统也可以通过context来封装以及限制程序的权限。要想弹出一个通知你必须通过这个api用户关闭你的通知权限你就别想通过第二条路来弹出通知了。同时 程序也无需知道底层到底是如何实现只管调用api即可。四大组件为何称为四大组件因为他们生来就有了context特别是activity和service包括Application。而我们写的一切程序都必须间接或者直接从其中获取context。
总而言之context就是负责区分android内外程序的一个机制限制程序访问系统资源的权限。