您的位置:首页 > 移动开发 > Android开发

android初级学习之深入理解Context

2016-11-21 21:46 375 查看
Context看起来很熟悉,基本每次进行android开发时都会用到,但是,我们又是否真的理解呢?例如,一个简单的问题,一个应用程序中到底有几个Context对象?一个,还是两个,还是多个?好吧,我们开始我们的Context之旅!


Contex的功能

这个就实在是太多了,例如弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等都需要用到Context。由于Context的具体能力是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。


Context是什么

直接翻译context就是“上下文”,当然比较多的还是“场景”。一个Context就意味着一个场景,而一个场景就是用户和系统交互的一个过程。如打电话时所看到的界面以及其背后隐藏的数据,这都可以称作是“场景”。这里,我们可以猜出Activity和Service也是一个场景。真的是这样吗?我们接着看一下Context的相关类。


Context相关类的继承关系



可以看到,Context的直系子类有两个,一个是ContextWrapper,一个是ContextImpl。那么从名字上就可以看出,ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类。而ContextWrapper又有三个直接的子类,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它只有一个直接子类,那就是我们熟悉的Activity。

由此,我们可以证明我们前面的猜测:Activity和Service本质就是一个Context,而另一个我们漏掉的是Application。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的。

其实,到这里,我们基本可以回答文章开始提出的问题了:一个应用程序的Context数量=Activity数量 + Service数量 + 1,其中因为一个应用程序只有一个Application。

好吧,既然Application、Activity、Service都是直接或间接继承自ContextWrapper的,我们就直接看ContextWrapper的源码。

ContextWrapper的实现

不同于ContextImpl,该类在android.content包中,在该类中,我们可以看到许多我们熟悉的方法:









……

可以看出Application方法的实现过程是这样的:



getApplica()和getApplicationContext()的区别:实际上这两个方法在作用域上有比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就只能用getApplicationContext()方法了

好吧,接着来看看ContextImpl。

ContextImpl的实现









…….

注意到ContextWrapper中的getApplica(),getApplicationContext()等方法在ContextImpl中都是借助对象mpackageInfo来实现的。那么mpackageInfo又是什么呢?

Application对应的Context

每个应用程序在第一次启动时,都会首先创建Application对象。如果对应用程序启动一个Activity(startActivity)流程比较清楚的话,创建Application的时机在创建handleBindApplication()方法中,该函数位于 ActivityThread.java类中。查看方法中重要的两行:


接着查看makeApplication()方法:



这里创建了一个ContextImpl对象,然后调用了init()方法,其中第一个参数this就是指的当前PackageInfo对象。接着新建一个Application对象,最后将将该Application实例传递给该ContextImpl实例。

接着看Activity对应的Context。

Activity对应的Context

通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调 onCreate(),onStart()方法等, 函数都位于 ActivityThread.java类 。

好吧,我们先看handleLaunchActivity()方法:



接着是performLaunchActivity()方法:



再看createBaseContextForActivity()方法:



最后来理一下整个流程:先是启动一个Activity,然后是创建一个Activity实例,接着是实例化ContextImola,最后将Activity信息传递给ContextImpl对象。

Service对应的Context

通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:

可以看出,先创建一个ContextImpl对象实例 ,然后初始化该ContextIml实例的相关属性,接着获得我们之前创建的Application对象信息,最后将该Service信息传递给该ContextImpl实例 。

另外,需要强调的是,通过对ContextImp的分析可知,其方法的大多数操作都是直接调用其属性mPackageInfo(该属性类型为PackageInfo)的相关方法而来。这说明ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的所有ContextIml实例,都对应同一个packageInfo对象。

其实,更多细节的东西都还在源码中……….
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: