Android中Context的使用总结 - Go语言中文社区

Android中Context的使用总结


一、Context继承体系

只用记住一句:Activity 、 Service 与Application 都是继承自ContextWrapper,而ContextWrapper implements Context。每个:Activity 、 Service 与Application都是一个Context实例。
Context 总数 = Activity个数 + Service 个数 + 1个ApplicationContext
可以通过命令行 查看Context的个数
adb shell dumpsys meminfo package_name

二、关于Context的疑问

1. getBaseContext 与 getApplicationContext 区别? 持有Activity的Context 相当于持有Context,而持有AppliactionContex全局仅有这一个
2. 视图中的Context从哪来的? 例如:new TextView(Context);
通常在一个Activity中传入的就是当前Activity或者Activity.getBaseContext(),所以通过View.getContext()其实就是当前Activity的引用。
常见场景,Adapter通常通过构造器传递Context,用于getView 时inflate 视图。但是getView最有一个参数是parentView 这个是ListView对象本身,可以通过parentView.getContext获取Context对象减少手动传递。
3. Context 会出错的地方 Dialog.Builder必须传入Activity,而不能传入Activity.getApplicationContext()

三、内存溢出,因为引用Context导致

1. Context导致内存溢出的原因:
最常见的内存溢出形式是Bitmap未得到释放,而图片通常ImageView持有导致ImageView也不会被GC释放,创建ImageView肯定需要Context,这个Context是Activity。 Bitmap -> ImageView -> Contex(Activity) 如果Activity总是不能得到释放,导致内存不足最终OOM
2. 对于生命周期很长的对象,使用ApplicationContext:
使用自定义Application,需要Context对象时传入,避免因持有Context导致的内存溢出。因为ApplicationContext全局仅有一个实例,而多个Activity本身继承自Context,就是多个Context实例。Android中Activity共享变量的另一方法:Application context
3.保存Context引用
在编写工具类时,我们经常会编写成单例的模式,而这些工具类大都会访问资源,也就是说需要Context的参与。这时就会涉及到Context的引用问题。比如下面工具类的写法:
public class Util {
    private Context context;
    private static Util util;
    private Util(Context  context){
        this.context = context;
    }
    public static Util getInstance(Context context){
        if(null == util){
            synchronized (Util.class) {
                if(null == util){
                    util = new Util(context);
                }
            }
        }
        return util;
    }
}
上面的工具类中,使用单例模式,内部保存了一个Context引用,表面上看这样的写法是没有问题的,但是实际应用中可能会造成内存的泄露。因为我们不能够确定传入的Context来自哪里,如果是在某个Activity中,直接传入的是this,这时这个Util类中是一个static修饰且强引用的是常驻内存的,它内部会一直持有这个Activity作为Context引用,这就会导致即使这个Activity被销毁掉,这个Activity还是没有办法进行内存回收。所以就造成了内存泄漏。
为了避免上面的问题,我们可以对getInstance方法进行优化,如下:
public static Util getInstance(Context context){
        if(null == util){
            synchronized (Util.class) {
                if(null == util){
                    util = new Util(context.getApplicationContext());
                }
            }
        }
        return util;
    }
采用上面的方式可以理解为引用的是ApplicationContext,它的生命周期与单例对象一致,只要这个应用还在运行这个Context就会一直存在。所以说当Application的Context能解决的情况下,优先使用Application的Context,这样能避免内存泄漏。

四、Context的应用场景

但是上面的解决方案并不是万能的,在涉及到UI加载操作,启动Activity等,使用Application的Context时,程序无法运行的。这主要是因为Activity和Application所代表的Context返回的不是同一个对象,它们各自的使用场景也是不同的。下面是它们各自的使用场景,可以进行比较。

——————-Context应用场景比较

一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:
数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。
从上面的表格中可以看出,和UI相关的方法都不可使用Application,都应该使用Activity作为Context来处理;然后再配合工具类中Context引用的持有,防止内存泄漏,这是便能达到好的应用效果。

参考文章:http://www.w2bc.com/Article/13096

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Allenshen2533/article/details/51705611
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-25 15:28:36
  • 阅读 ( 1525 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢