Android中,Context、getApplicationContext()、 getApplication()以及Context方面的内存优化

context的意思是上下文,环境

Context可能是Android应用中最常用的元素,而它也可能是最容易误用的。

Context对象是如此常见和传递使用,它可能会很容易产生并不是你预期的情形。加载资源、启动一个新的Activity、获取系统服务、获取内部文件路径以及创建view(其实还远不止这些)统统都需要Context对象来完成。我(原文作者)想做的只是给大家提供一些Context是如何工作的见解,以及让大家在应用中更有效的使用Context的技巧。

Context的类型

并不是所有的Context实例都是等价的,根据Android应用的组件不同,你访问的context推向有些细微的差别

    在Android中,Context被用在许多操作中,但多数是为了加载或访问资源。这就是为什么所有的Widget组件都会在构造器中接受一个Context参数的原因所在。对于一般的Android应用有两种Context,即Activity和Application。开发者需要contex在类或方法间传递时会选择Activity。

这表明Views 对整个Activity有一个引用,因此你的Activity中任何东西都会被View持有,一般被持有的是视图层级和它对应的资源。因此,如果你泄露了Context,你就泄露了一块内存。如果你不细心,泄露一个Activity就非常同意。

    当屏幕方向切换时,系统默认会保存它的状态,销毁当前Activity并重新创建一个。为了做到这一点。Android会从资源中重新加载应用程序的UI。现在假设你开发一个应用,包含了大量位图Bitmap,你不想每次屏幕切换时都去加载位图,很简单的方法就是利用static静态域。

上面的代码非常简洁,也非常容易出错:第一次屏幕方向改变时,最初创建的Activity(每次屏幕改变都会重新创建Activity)就会出现内存泄露。但一个Drawable对象被附着到View中,View就会在这个Drawable上被设定作为回调callback。上面的代码段表明drawable对TtextView有一个引用,而TextView本身对Activity持有引用,返回来,Activity又对许多东西持有。

  上面的解决方案是:当Activity被销毁时,设置被存储的drawable的回调为null。

 

 

 在语句AlertDialog.Builder builder=new AlertDialog.Builder(this)中,要求传递的参数就是一个context,在这里我们传入的是this,那么这个this究竟指的是什么东西?

这里的this指的是Activity.this,是这个语句所在的Activity.this,是这个Activity的上下文。网上有很多朋友在这里传入的是this.getApplicationContext(),这是不对的。AlertDialog对象是一个依赖于View 的,而View是和一个Activity对应的。于是,这里涉及到一个生命周期的问题,this.getApplicationContext()取得是这个应用程序的Context,Activity.this取的是这个Activity的Context,这两者的生命周期是不同的。前者的生命周期是整个应用,后者的生命周期只是它所在的Activity。而AlertDialog应该是属于一个Activity的,在Activity销毁的时候它也要销毁,不存在了。但是,如果传入this.getApplicationContext(),就表示它的生命周期是整个应用程序,这显然超过了它的生命周期了。所以,在这里只能使用Activity.this。

new AlertDialog.Builder(getApplicationContext())时发生错误:

E/AndroidRuntime(5844): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application

于是查了查:

getApplicationContext() 生命周期是整个应用,应用摧毁它才摧毁 Activity.this的context属于activity ,activity 摧毁他就摧毁

activity.this要返回一个activity,而getApplicationContext()就不一定返回一个activity。

 

getApplicationContext()返回应有的上下文,生命周期是整个应用,应用销毁它才销毁。

Activity.this的context返回的是当前Activity的上下文,属于activity,activity销毁它就销毁。Activity.ths在Activity当中可以缩写为this

getBaseContext()返回由构造函数指定setBaseContext()设置的上下文。

getApplication():android开发中共享全局数据

我们在平时的开发中,有时候可能会需要一些全局数据,来让应用中的所有Activity和View都能访问到,大家在遇到这种情况时,可能首先会想到自己定义一个类,然后创建很多静态成员,不过android已经为我们提供这种情况的解决方案:在Android中,有一个名叫Application 的类,我们可以在Activity中使用getApplication(),方法来获得,它是代表我们的应用程序的类,使用它可以获得当前应用的主题,资源文件中的内容等,这个类更灵活的一个特性就是可以被我们继承,来添加自己的全局属性。

有两种简单的方式去避免Context相关的内存泄露。最明显的一种就是避免Context超出它的范围。上面的例子展现了静态引用的情况:内部类以及对外部类的间接引用是很危险的。第二种方案是用Application。这种Context不依赖于Activity的生命周期,而是与你的应用程序同生共死。如果你打算持有一个长期活动并且需要Context引用的对象,记得使用Application对象。你可以通过调用Context.getApplicationContext() 或者 Activity.getApplication()方法得到它。

总的来说,要避免Context相关的内存泄露,铭记一下几条:

1不要对Activity(Activity 继承自Context)作长期的引用。一个指向Activity的引用于Activity本身有相同的生命周期

2 试着用application代替activity

3 如果你不能控制内部类的生命周期,避免使用非静态内部类,应该用静态内部类。并且对里面的Activity做弱引用。该问题的解决方案是:对于外部类,用WeakReference构造静态内部类,同时要再视图里完成。并且他的WeakReference内部类有一实例

4 垃圾回收不是防止内存泄露的保险方式。

 

posted @ 2015-08-05 14:54  大大的海棠湾  阅读(376)  评论(0编辑  收藏  举报