Ⅰ Context是什么
Context
Context,翻译是上下文环境,在android应用开发中的地位举足轻重,甚至源码也有一句话:everything needs a context(看到过,但是忘了在哪里了)。
从这个类图中我们看到,Context是一个抽象类,一边是Context的具体实现ContextImpl,一边则是组件的封装类ContextWrapper,便于二者各司其职,我理解是接口和实现分开,这样比较符合开闭原则吧。
既然要搞清楚Context是干嘛的,得先看Context的实例什么时候创建的。这里有两个我们经常接触的组件,Activity和Service,以及用的较少的Application,它们的关系如下:
1.Application:保存应用的全局信息的类,它的实例在应用创建的时候被创建,生命周期最长,应用销毁了才会被销毁。
2.Activity:创建一个Activity便对应一个Context的实例,生命周期随着Activity的周期变化。
3.Service:创建一个Service便对应一个Context的实例,生命周期随着Service的周期变化。
下面逐一看看它们是什么时候实例Context类的,我们需要知道,一个ContextWrapper类对应一个ContextImpl类的实例,因为具体实现的地方还是在ContextImpl中,ContextWrapper中的mBase变量指向ContextImpl变量的实例。
(1) Application的Context创建:
如图:
跳过Process创建过程,在我们的应用被zygote进程孵化出来后,被反射调用main函数,这里会创建ActivityThread类的实例,该类并不是一个线程类的子类,但是它的main函数跑在新创建的进程的主线程。 创建后,调用attach方法,与AMS绑定:
这里传入的mAppThread变量为ApplicationThread类,它为ActivityThread的内部类,作用就是作为应用端的binder服务端,负责接收AMS的调度,这里我们传入这个服务端的句柄,让AMS持有,从而AMS可以通过这个句柄来操控应用的行为。我们向AMS请求,则是通过AMS的句柄。接下来就到了AMS的逻辑:
在ApplicationThread的实现端中,就是把跨进程传递过来的各种数据再用一个AppBindData类保存下来,然后调用Handler对象H发送消息BIND_APPLICATION,最后在app线程中处理此逻辑,让AMS线程可以不必同步阻塞。接下来就到了handleBindApplication:
Instrumentation类是用于管理Acitivty的工具类。这又有一个比较重要的对象出现,LoadedApk,它里面保存了类加载器ClassLoader,data.info的对象便是它,makeApplication函数中的逻辑:
这里我们看到了ContextImpl的创建时机,就是在Application实例创建的时候:
如此就完成了Application的创建,并且调用attach方法把Application的mBase对象赋值给创建的ContextImpl,至此Application的创建就完成了,getApplicationContext() 返回的便是此处我们创建的Application类的对象。
这里为什么要去LoadedApk类中利用类加载器,把对象创建出来呢?因为我们的Application类是可以自己拓展的,创建的时候是不确定应用自己是否复写了Application对象, 利用类加载器就可以动态决定创建什么类的对象了 ,我们只需要从PMS中获取到application的具体类名即可,这个类名是写在Mainfest文件中的,后面Activity也是利用这种机制去创建对象。
(2)Actvity的启动过程,网上非常多文章分析了,我也就不再重复罗列出来了,大概的过程就是startActivity()被调用,然后检查目标Acitivity的进程是否创建,没创建就先向Zygote进程请求fork应用进程,然后一系列初始化过程后,最后在AMS模块中的ActivityStackSupervisor中realStartActivityLocked()调用IApplicationThread代理类,调用到scheleLaunchActivity(),然后在应用的线程开始执行handleLaunchActivity(),最主要的逻辑在performLaunchActivity:
至于Activity的生命周期后面怎么走的,这里不在乎,我们只看Context实例化过程。同样的,这里也会创建ContextImpl对象,在Activity对象创建后,调用Attach(),这个函数挺重要的,我们看看都干了什么:
在attach函数中,也会为ContextWrapper中的mBase对象赋值一个ContextImpl对象,我们这里也能猜想到Service的创建估计也会有这个过程,而事实上Service的创建差不多也是这个过程,所以不再赘述了。
我们可以知道Context例的实例化都是在我们在Application,Activity和Service创建的时候会实例化,而这些组件的父类都是ContextWrapper,所以在创建的时候还会先创建一个ContextImpl类对象,然后给自己的父类mBase变量赋值,既然如此,Context的引用对应的就是Application,Activity和Service了,Context的用处就是提供了一组抽象的函数,让子类去相对应的实现,当你持有一个Context引用的时候,你可以通过这个引用去获取相对应组件的信息。比如持有一个Activity的Context引用,你可以在别的地方调用startActivity()去启动一个新的activity。
总结:
1.Context是抽象类,所以实例化要在子类中,Application,Service,Activity是我们实例化的地方,一般应用创建会实例化一个Application类,Service和Activity则是在startService和StartActivity被调用的时候会实例化,它们都会创建一个ContextImpl类实例,给自己的父类ContextWrapper的mBase变量赋值。
2.Context是组件中通用操作的一组集合,当具体的子类实例化后,可以在别的地方通过保存一个Context引用去获取信息和通用操作等,也就是说,我们可以通过这个引用去获取到这个应用中的重要信息以及这个应用的通用操作。
Ⅱ 源码分析->一个应用到底有几个Context
相信很多人都知道是这样计算的,那到底为什么是这样呢?
源码分析基于Android28源码
什么是Context呢?可以理解为上下文、运行环境,当需要获取资源、系统服务以及启动Activity、Service用到,也可以通过它跟系统交互。
通过以下继承关系可以看出,Activity是继承ContextWrapper
ContextWrapper内部有一个Context类型的成员变量mBase
mBase是通过attachBaseContext()方法赋值
是创建Activity的关键,
主要工作
(1)createBaseContextForActivity()内部实例化ContextImpl 对象;
(2)mInstrumentation.newActivity()内部通过反射实例化Activity对象;
(3)activity.attach()内部会调用attachBaseContext()方法给mBase对象赋值;
通过以下继承关系可以看出,Application是继承ContextWrappe
是创建Application的关键,
主要工作:
(1)ContextImpl.createAppContext()实例化ContextImpl ;
(2)mActivityThread.mInstrumentation.newApplication(),内部通过反射实例化Application,并把appContext传递过去,通过attach()方法给mBase赋值;
跟Activity类似就不再做分析。
经过分析发现:
1.每个Activity,Service,Application都有一个ContextImpl 类型的成员变量mBase,ContextImpl是Context的实现类。
2.细心的读者可能发现,Activity,Service,Application都是继承Context,其实他们本身是一个Context,也都实现了Context的抽象方法,
那么一个Activity是否就拥有两个Context呢?
是不是
这样计算比较合适呢?
下面看下Context中常用的三个方法,
ContextImpl继承Context,并实现了这三个方法,
Activity间接继承Context,主要是在ContextWrapper实现了以上三个方法,从源码中可以看出,最终还是调用了ContextImpl的实现。
下图可以看出这几个的关系,ContextWrapper顾名思义就是Context的包装类(有ContextImpl的成员变量),并且实现了Context,这是一种装饰者设计模式。当在Activity中调用getAsset()时,其实最终是调用mBase的getAsset()。
Activity间接继承了Context,是为了拥有跟ContextImpl一样的功能,但真正起作用的是mBase这个成员变量,所以一个Activity其实就只有一个Context起作用,那就是ContextImpl类型的mBase。
这种计算方法应该是没有问题呢。
或许有人有这样的疑问,一个应用不是只有一个Application吗,为什么计算公式是加上Application个数?单进程应用来说,一个应用确实只有一个Application,而多进程应用,那么一个应用就有多个Application,所以应该说一个应用有一个或多个Application,一个进程有一个Application。
另外其他关于Context的常见面试题
1.Activity的this跟getBaseContext区别。
前者是Activity对象本身,后者是通过attachBaseContext传入的ContextImpl对象mBase,两者功能是一样的,通过this最终还是会调到mBase对象中。
2.getApplication和geApplicationContext区别。
两者都是返回Application对象,前者是Activity和Service里面的方法,后者是Context中定义的方法。
3.应用组件的构造,onCreate、attachBaseContext的执行顺序。
先是组件构造化,接着attachBaseContext,传入ContextImpl对象,最后是onCreate方法。
4.谈谈你对Context的理解
先是Context的作用,然后是有几种Context,Application、Service、Activity的Context有什么区别以及继承关系,
最后是mBase变量是如何实例化的。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
Ⅲ Android 关于ContextWrapper的问题
ContextWrapper类 路径 :\frameworks\base\core\java\android\content\ContextWrapper.java
说明: 正如其名称一样,该类只是对Context类的一种包装,该类的构造函数包含了一个真正的Context引用,即ContextIml对象。
Ⅳ Context摘要(一)
ContextImpl和ContextWrapper都是继承自Context,在ContextWrapper中依赖了ContextImpl对象(mBase)。这里使用了装饰者模式,ContextWrapper是装饰类,对ContextImpl进行包装,通过使用ContextImpl实现功能。
Application、Service、ContextThemeWrapper继承自ContextWrapper,它们也是通过ContextImpl实现功能,同时在ContextWrapper的基础上添加了自己的功能。此外ContextThemeWrapper中包含了主题相关的方法,Activity继承自ContextThemeWrapper。