美文网首页Android干货Android Class程序员
如何在你的应用中正确使用Context

如何在你的应用中正确使用Context

作者: MeloDev | 来源:发表于2016-04-19 10:38 被阅读2204次

    如何在你的应用中正确使用Context

    写在前面:

    Context对象在我们的项目中实在是太常见了,启动Activity、Service、发送一个Broadcast,作为获取各种系统Resources的参数,Layout Inflation的参数,show a Dialog的参数等等。Context对象的使用不当,是可能造成内存泄漏的,当你的工程代码已经达到十几万行甚至是几十万行时,Context对象就对内存泄漏造成非常可观的影响了,所以我们应该对Context对象的使用,做到心中有数。

    零:什么是Context

    前两天刚刚对 Context 写了一篇比较长的博客,借鉴大牛们的经验,对 Context 进行了比较详细的整合与总结,花半个小时的时间耐心读一读吧!

    你足够了解 Context 吗?

    一句话总结:

    Context是为一个Android程序提供各种功能、资源、服务的一个环境, Context 的资源在系统中只有一套,因为它的子类(Application、Activity、Service)对这同一块资源处理方式的不同,让Context 对象在功能上表现出各自之间的差异

    一:Context对象之间的差异

    相信如果你是一个初学者, Context 在你手里应该是胡乱传入的,哪里有 Context 就找哪里,各种this乱入,O(∩_∩)O哈哈,至少当时我是这样的,但是 Context 不同的对象在使用功能上是有区别的,比如以下代码:

    获得Application的Context

    在清单文件中做以下配置:

    这里写图片描述

    在界面中show a Dialog

    show a Dialog

    点击按钮之后崩溃信息

    崩溃信息

    当我使用ApplicationContext时,是无法弹出一个Dialog的,因为Dialog作为一个View,依附在Activity上,并且与Theme相关,当传入参数为ActvityContext时,崩溃就解决了。

    下面这张表展示出了Context对象之间使用上的差异

    这里写图片描述

    二:Context相关的内存泄漏问题

    在讨论内存泄漏之前,先简单的说说Android中内存的回收

    Dalivik虚拟机扮演了常规的垃圾回收角色,为了GC能够从App中及时回收内存,我们需要时时刻刻在适当的时机来释放引用对象,Dalvik的GC会自动把离开活动线程的对象进行回收。

    什么是Android内存泄漏:

    虽然Android是一个自动管理内存的开发环境,但是垃圾回收器只会移除那些已经失去引用的不可达的对象,在十几万、几十万行代码中,由于你的失误使得一个本应该被销毁的对象仍然被错误的持有,那么该对象就永远不会被释放掉,这些已经没有任何价值的对象,仍然占据聚集在你的堆内存中,GC就会被频繁触发,多说几句,如果手机不错,一次GC的时间70毫秒,不会对应用的性能产生什么影响,但是如果一个手机的性能不是那么出色,一次GC时间120毫秒,出现大量的GC操作,我相信用户就能感觉到了吧。这些无用的引用堆积在堆内存中,越积越多最终导致Crash,有关一些性能优化推荐给大家一个我总结的博客。

    • 错误的单例模式
    错误的单例模式

    我们来分析一下这个非线程安全的单例模式,假设你在Activity A去getInstance获得instance对象,顺手传了一个this,好了,现在一个常驻内存的Singleton保存了你传入Activity A的对象,并且一直持有Activity A的引用,这样即使你Activity被销毁掉,但是因为它的引用还存在于一个Singleton中,是不可能被GC掉的,这样就导致了内存泄漏。

    • View持有Activity的引用
    这里写图片描述

    再来分析一下,有一个静态的Drawable对象,当我给ImageView设置这个Drawable时,ImageView像上面那个例子一样,保存了这个mDrawable的引用(大家可以点开源码705行去看,很多UI组件都是统一的操作,一直持有传入的对象),然而ImageView传入了this,也就是ImageView同样持有一个MainActivity的mContext。因为被static修饰的mDrawable是常驻内存的,MainActivity是它的间接引用,所以当MainActivity被销毁时,也不能被GC掉,所以也造成了内存泄漏

    三:使用Context的正确姿势

    通俗一点说,Context造成的内存泄漏,几乎都是当Context销毁的时候,却还被各种不合理、无端地引用着。那么哪个Context对象是不会被销毁的呢?对了,Application的Context对象可以理解为随着进程存在的,所以当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context

    这里写图片描述

    调用一行代码:

    LaucherApplication.getContext();

    回头看看上面那张表格,显然Application的Context不是万能的,涉及UI加载操作时,似乎我们只能使用Activity的Context,所以你当你使用Activity的Context时,你要对持有Activity的对象心中有数,保证它能随着生命周期的销毁而被回收,慎用static关键字,不要因为方便访问就各种static乱入。

    多说一点,上表中Layout Inflation中只能使用Activity的Context,而各种View在创建时,需要传入的Context参数也是Activity的,大家懂了吧,当解析XML文件的时候,传入的参数也就统一了,相信大家一定能想明白这点。

    写在最后:

    给大家推荐一个内存检测的自动化工具,LeakCanary,但是当你曾经写出的代码不规范不负责,已经达到十几万行,几十万行的时候,再去抽丝剥茧试图解开已经打上层层死结的引用关联,是非常难的。所以平时还是要注意下细节哈~

    相关文章

      网友评论

      • 天蝎_少:上表中Layout Inflation中只能使用Activity的Context,而各种View在创建时,需要传入的Context参数也是Activity的,
        LayoutInflater.from(context)中可以传application吧.源码注释:
        The Context object for your activity or application.
      • 发黄的小草:Mark 写的不错!
      • qingfeng825:你跟Stormzhang什么号关系?
        MeloDev:@qingfeng825 额朋友,什么震惊的吗,。
        qingfeng825:@qingfeng825 嗯 昨天看了你们俩的内容我都惊呆了
        MeloDev:@qingfeng825 我给他投稿啊
      • wallace_dong:用dagger2
      • 2fb6988652a3:来说两句~~~头像很帅的样子
      • 6ee72519b51d:两篇都看完了,棒棒的

      本文标题:如何在你的应用中正确使用Context

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