Android中Context的使用总结
16lz
2021-01-25
一、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
更多相关文章
- Python3原生编写月份计算工具
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- 初试GreenDAO 3.2.3
- Android(安卓)在Java代码中设置style属性--使用代码创建Progress
- Android(安卓)Toolchain与Bionic Libc
- 【java】【android】序列化Serializable和继承
- Android(安卓)Kernel - Boot Loader