美文网首页
搞懂Context

搞懂Context

作者: 金馆长说 | 来源:发表于2018-08-15 19:12 被阅读11次

    参考资料

    如果我们通过这种new的方法是否能启动activity,答案是不能的。

    Activity act = new Activity();
    

    平常我们在编写代码的时候,很大部分都是通过new关键字来创建我们的需要的对象完成需求。但是在android想启动一个activity或者其他组件,这就行不通了。这里说到了组件对android的应用程序设计模式就是,基于组件的设计模式。它需要我们其他一个完整的android工作环境,在这个环境下组件才能正常的工作,这里就需要到了这个Context了。

    Context的职责
    Context是android程序中一个很重要的一个类,四大组件的使用,或者我们的view控件使用都需要它。context可以理解为一个摄影机,程序就是一部电影,程序中的组件的运行都需要在context这个摄影机下面运行,如果没有摄影机它们的运行就没有意义了,因为程序是一步电影。英文翻译过来就是上下问的意思,我们的程序都需要依赖它来运行,它是组件的一个运行环境。

    Context集成结构

    Context作用域

    虽然Context神通广大,但并不是随便拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限制的。由于Context的具体实例是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

    从上图我们可以发现Activity所持有的Context的作用域最广,无所不能。因为Activity继承自ContextThemeWrapper,而Application和Service继承自ContextWrapper,很显然ContextThemeWrapper在ContextWrapper的基础上又做了一些操作使得Activity变得更强大,这里我就不再贴源码给大家分析了,有兴趣的童鞋可以自己查查源码。上图中的YES和NO我也不再做过多的解释了,这里我说一下上图中Application和Service所不推荐的两种使用情况。

    1. 如果我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式不推荐使用,Service同Application。
    1. 在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。

    一句话总结:凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。

    各个Context的作用域

    如何获取Context

    通常我们想要获取Context对象,主要有以下四种方法

    1. View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。

    2. Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。

    3. ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。

    4. Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使用ApplicationContext也可以。

    正确使用Context

    一般Context造成的内存泄漏,几乎都是当Context销毁的时候,却因为被引用导致销毁失败,而Application的Context对象可以理解为随着进程存在的,所以我们总结出使用Context的正确姿势:

    1. 当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。

    2. 不要让生命周期长于Activity的对象持有到Activity的引用。

    3. 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

    一个应用有几个Context

    其实这个问题本身并没有什么意义,关键还是在于对Context的理解,从上面的关系图我们已经可以得出答案了,在应用程序中Context的具体实现子类就是:Activity,Service,Application。那么Context数量=Activity数量+Service数量+1。当然如果你足够细心,可能会有疑问:我们常说四大组件,这里怎么只有Activity,Service持有Context,那Broadcast Receiver,Content Provider呢?Broadcast Receiver,Content Provider并不是Context的子类,他们所持有的Context都是其他地方传过去的,所以并不计入Context总数。上面的关系图也从另外一个侧面告诉我们Context类在整个Android系统中的地位是多么的崇高,因为很显然Activity,Service,Application都是其子类,其地位和作用不言而喻。

    相关文章

      网友评论

          本文标题:搞懂Context

          本文链接:https://www.haomeiwen.com/subject/outgbftx.html