一、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返回的不是同一个对象,它们各自的使用场景也是不同的。下面是它们各自的使用场景,可以进行比较。

——————-

一些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

更多相关文章

  1. Python3原生编写月份计算工具
  2. 一款常用的 Squid 日志分析工具
  3. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  4. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  5. 初试GreenDAO 3.2.3
  6. Android(安卓)在Java代码中设置style属性--使用代码创建Progress
  7. Android(安卓)Toolchain与Bionic Libc
  8. 【java】【android】序列化Serializable和继承
  9. Android(安卓)Kernel - Boot Loader

随机推荐

  1. Android开机自启动程序设置及控制方法
  2. Android随笔之——Android时间、日期相关
  3. Android工程师怒斥360拦截弹窗广告
  4. Android(安卓)动画实现弹幕效果
  5. 用Android模拟器体验angry birds
  6. Android 页面回调跳转(startActivityForRe
  7. 64位 ubuntu android SDK 无法新建AVD,adb
  8. Android(安卓)view的预加载占位效果
  9. 从零开始--系统深入学习android(实践-让我
  10. Android绘图之drawText绘制文本相关(4)