Context :上下文; 本文章专门对Context体系进行详细了解
一. Context 的类继承图
-- Context
---ContextImpl
---ContextWrapper
-----Application
-----ContextThemeWrapper
---Activity
-----Service
1.1 ContextImple负责Context核心功能的实现,而我们常见的Activity Context,Application Context …则是继承ContextWrapper(Wrapper:包装类后缀,由此可以推测,使用了包装设计模式,继承于Context,同时有一个Context的引用),以下是源码:
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
}
通过以上的类图,我们的 Application、service、Activity都是继承于ContextWraper
二.Context初始化
2.1.Activity
ActivityThread.performLaunchActivity()是Activity的启动方法:
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
//创建了ContextImple
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
appContext.setOuterContext(activity);
activity.attach(appContext, xxxx);
获得LoadedAkp实例,接着调用createBaseContextForAcivity(),创建了ContextImple实例;通过Activitiy#attach(ContextImple,xxx)方法,让ContextWraper 获得ContextImpl 的引用
2.1.1 Q:ContextImple 怎么创建的?
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId;
try {
displayId = ActivityManager.getService().getActivityDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
//这里创建的
ContextImpl appContext = ContextImpl.createActivityContext(xxxxx);
static ContextImpl createActivityContext(xxxx....)
{
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
......
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
}
2.1.2 Q:Activitiy 是如何attach(mBaseContext)的?
2
3
final void attach(context) {
attachBaseContext(context);
}
protected void attachBaseContext() {
super.attachBaseContext(newBase);
}
2.2 Q:Service是如何获取ContextImple 的引用的?
Service 是ActivityThread#handleCreateService()创建的
2.2.1 handleCreateService源码
handleCreateService()
{
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//反射得到的Service
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
....
}Catch(Exp e){
}
}
简单的说就是:
初始化LoadedApk–>ContextImpl.createAppContext(this, LoadedApk)获得ContextImpl–>反射获得的Servicea,attch获得该引用;
而且attach的方法也是调用ContextWrapper的方法
attach
public final void attach() {
attachBaseContext(context);
}
三.Application
Application的创建是在LoadApk#makeApplication
3.1 makeApplication 的源码
public Application makeApplication(){
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
//反射的方法创建Applicaton
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//这里使用createApplicationContext 创建
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
}
}
貌似这里没有attach方法,查阅资料说在Instrumentation#newApplication()方法里面
newApplication
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
/**
* @hide APP类
*/
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
最后发现还是使用了ContextWrapper#attach 方法
creatApplication
static ContextImpl createAppContext() {
ContextImpl context = new ContextImpl();
context.setResources(packageInfo.getResources());
}
四.小结
- Context 是最终父类,分别有主要实现类ContextImple,和使用包装设计模式的ContextWrapper,他需要ContextImpl的引用;
- Activity,Service,Application的大致流程:
1.在首先需要LoadedApk,(前两者在各自方法中获得,后者直接在LoadedApk创建)
2.通过反射构建自身实
3.以LoadedApk作为参数构建ContextImple
4.最终都是调用ContextWrapper的attach(context)方法获得引用的(这里符合单一责任原则)
五.实践
5.1 View.getContext()返回当前View使用的Context实例,这个实例主要用于获取View所要使用的资源信息,那么这个Context实例是什么时候赋值的,使用的是那种类型的Context,有什么区别呢?
5.2 View的mContext 是LayoutInflater.from(context)传进去的
{
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.xxx,null);
}
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
进一步在ContextImpl实例中,由ContextImple实现
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
可以看到最终的调用都转发给了 ServiceFetcher,而 SYSTEM_SERVICE_FETCHERS 则是存储着各个类型的 Fetcher 的实例
可以看出:
SYSTEM_SERVICE_FETCHERS 作为一个HashMap存储着Service名称和Fetcher的映射关系,而这种关系的注册是用过registerService()注册的;
进一步
LayoutInflater的实现类是PhoneLayoutInflater,同时Fetcher的实现类是CachedServiceFetcher
现在看CachedServiceFetcher.getService()
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
Object service = cache[mCacheIndex];
if (service == null) {
try {
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
}
}
return (T)service;
}
}
以下是分享LayoutInflater中inflat
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
// mContext 是具体 ContextWrapper 实现类的实例,上文我们已经提到了
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// 解析 xml 中的节点
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
} catch () {
} finally {
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
return result;
}
}
所以我们是createViewFromTag的方法里面根据标签名称去实例化具体的View实例的
createViewFromTag(){
view = createView(name, null, attrs);
}
该方法又是调用creatview方法创建View实例的
public final View createView(String name, String prefix, AttributeSet attrs){
// 缓存构造函数
Constructor<? extends View> constructor = sConstructorMap.get(name);
// 检查构造函数类加载器
if (constructor != null && !verifyClassLoader(constructor)){
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
// 获取 View 构造函数实例,并处理过滤情况
if (constructor == null) {
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {
// 检查是否过滤
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
if (mFilter != null) {
// 存在过滤器
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// 没有缓存过滤结果,新的类
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
Object lastContext = mConstructorArgs[0];
if (mConstructorArgs[0] == null) {
// 如果还没设置 context 参数
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// 反射创建 View 实例,这里调用的构造函数类型为 (Context,AttributeSet),从 args 可知
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// 使用相同的 Context 处理 ViewStub
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;
} catech () {
}
}
七.Application Context 和Activity Conteext 用在View Context中由什么不同的地方呢?
protected void onCreate(Bundle savedInstanceState) {
// 使用两种 Context 创建两个 EditText
addEditText(this);
addEditText(getApplicationContext());
}
private void addEditText(@Nullable Context context) {
if (mRootLayout == null) {
return;
}
if (context == null) {
return;
}
EditText editText = new EditText(context);
mRootLayout.addView(editText);
editText.setText(context.toString());
}
`
Activity Context 的Editext使用theme中的配置,回顾我们最开始的Context类图,Acitvity基础于ContextThemeWrapper,而Application则继承于ContextWrapper,那我们是不是可以猜测ContextThemeWrapper的继承类才会读取theme中配置信息?
ContextThemeWrapper中的源码
public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
if (mThemeResource == 0) {
mThemeResource = R.style.Theme_AppCompat_Light;
}
initializeTheme();
return mTheme;
}
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
onApplyThemeResource(mTheme, mThemeResource, first);
}
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
theme.applyStyle(resid, true);
}
mThem和mThemeResource都可以通过构造方法和set方法进行赋值,假如没有给mTheme赋值,只给mThemeResource赋值,那么上面代码将mThemeSource应用到ContextWrapper.getTheme()返回的Themes实例,从而获得新的Theme实例.Activitiy的启动流程是在ActivityThread.performLauncheActivity中,其中涉及了Theme的赋值:
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
八小结
- 当使用LayoutInflater从xml文件中inflate布局时,调用的是View(Context,Attributeset)构造函数,使用的Context实例跟LayoutInflater创建时使用的Context一样的,并且LayoutInflater会缓存在Context实例中,相同的Context实例多次调用一样的LayotuInflater实例
- Activity Context 会读取Theme的样式,而
九.getApplication()和getApplicationContext()
getApplicationContext()的存在是Android历史原因,getApplication()这个只存在Activity和Service类中,那么对于BroadcastRecervice和ContentProvider来说,只能使用getApplicationContext();
两者对比:
1.对于Activity和Service而已,没有区别
2.BroadcastReceiver只能通过getApplicationContext()获得Application
3.ContentProvider只能通过getApplicationContex(),但是有可能出现控制.当同个进程有多个apk的情况,对于第二个apk是由provider方式拉起,而provider创建过程中并不会出现初始化Application,此时调用getApplicationContext()是空置
Dialog Context
dialog实例的创建必须是Activity Context ,如果传进去别的,则乎异常
start Activity
在application 中是ContextImp实现的,如果使用Application启动Activity.在26上需要体检Intent.FLAG_ACTIVITY_NEW_TASK
总结:
1.Context的类图:ContextImp主要实现,ContextWrapper使用包装设计模式
2.Activity,Service,Applciation;构建过程基本相同,同时创建LoadedApk,发送创建实例,attack方法
3.View中的Context 就是Layoutinflater中的Context,通过Layoutinflater.from(context)中传间的.而LayoutInflater只有context相同,他就相同;
4.Activitiy 由于在ActivityThread.performLauncheActivity方法中setTo了Theme,所以View使用了该Context,会默认调用Theme的样式,而使用Application则不需
5.对于获得Context:Activity和Service没有区别;而BroadcastReceiver 和ContextProvider 只能getApplicationContext获得;后者在同一个进程多个apk并且使用Contentprovider拉起的时候,Context是空置
6.Dialog 需要Activity Context;
7.在Appllication 中启动startActivity;只在26版本时候异常,其他没问题
[参考]通过该文章写的笔记,以后争取水平越来越高,自己总结知识(https://linxiaotao.github.io/2018/04/12/%E7%86%9F%E6%82%89%E5%8F%88%E9%99%8C%E7%94%9F%E7%9A%84Context/)