Android性能优化(二)——context 引起的内存问题

首先,要搞清楚的是Android中获取上下文的方式:

  1. this 或者 getContext()
  2. getApplicationContext()
  3. getBaseContext()
  4. getApplication()(严格来说,这个方法不能得到程序的Context)

第一种:this or getContext():
谷歌文档给出的解释如下:

Returns the context the view is running in, through which it can access the current theme, resources, etc.
意思是:返回当前Activity的context,通过它可以得到当前的theme,resources等资源。
特点:属于当前Activity,随着当前Activity的销毁而销毁

第二种:getApplicationContext()
谷歌文档给出的解释如下:

Return the context of the single, global Application object of the current process. This generally should only be used if you need a Context whose lifecycle is separate from the current context, that is tied to the lifetime of the process rather than the current component.

Consider for example how this interacts with
registerReceiver(BroadcastReceiver, IntentFilter):

  • If used from an Activity context, the receiver is being registered within that activity. This means that you are expected to unregister
    before the activity is done being destroyed; in fact if you do not do
    so, the framework will clean up your leaked registration as it removes
    the activity and log an error. Thus, if you use the Activity context
    to register a receiver that is static (global to the process, not
    associated with an Activity instance) then that registration will be
    removed on you at whatever point the activity you used is destroyed.
  • If used from the Context returned here, the receiver is being registered with the global state associated with your application.
    Thus it will never be unregistered for you. This is necessary if the
    receiver is associated with static data, not a particular component.
    However using the ApplicationContext elsewhere can easily lead to
    serious leaks if you forget to unregister, unbind, etc.

文档意思如下:
调用getApplicationContext() 将会返回当前进程所代表的应用的Context,它是一个全局的Context。
它通常只在这种情况下使用:如果你需要一个Context它的生命周期不同于当前的context,也就是说,它是和整个应用程序的进程绑定在一起的而不是当前的Activity。
我们必须思考这样一个问题:getApplicationContext()和registerReceiver(BroadcastReceiver, IntentFilter)的交互问题:

第一,当你在Activity的Context中使用了registerReceiver那么这个receiver就是被注册在了这个Activity中,在这种情况下当Activity被destoryed的时候必须unregister,如果没有这样做便产生了内存泄露,然而framework在清除这个Activity的同时清除了这个泄露的registration并且会打印一个错误log信息(framework清除了这个泄露,给我们以没有内存泄露的假象)。那就是说,即便在Activity的Context中register了一个静态的receiver(存在于整个进程中,与单独的Activity实例没有任何联系),在任何destory这个Activity的时候它都会被移除掉。

第二,当你使用getApplicationContext()注册了一个receiver,那么这个receiver便和整个application绑定在了一起处于一种全局的状态。因此它不会随着Activity的destory而unregister。如果用getApplicationContext() 注册了一个receiver它应该和一些静态的数据关联在一起而不是通常的那些组建(我理解是Android中的view)。然而,当你在程序中到处使用ApplicationContext这样很容易引起内存泄露如果你忘记unregister,unbind。

第三种:getBaseContext()
谷歌文档给出的解释如下:

the base context as set by the constructor or setBaseContext
意思如下:
调用getBaseContext()将返回一个base context, 在构造方法和setBaseContext方法中被set的。

第四种:getApplication ()
谷歌文档给出的解释如下:

Return the application that owns this activity.
意思是:调用getApplication 返回一个application 它拥有则个Activity。

他们的使用方式区别:

  1. 在开发中,有时候需要一些全局数据,来让应用中所有Activity和View都能访问到,andorid为我们提供了这样的解决方案:在Android中,有一个名为Application的类,我们可以在Activity中使用getApplication(),方法来获得,它是代表我们的应用程序的类,使用它可以获得当前应用的主题,资源文件中的内容等,这个类更灵活的一个特性就是可以被我们继承,来添加我们自己的全局属性。
  2. 如果有一个全局的数据库操作类,用到了context, 这个时候就要用到getApplicationContext() , 而不是用Activity, 这就保证了,数据库的操作与activity无关。
  3. Activity是跟Windows相关,而getApplicationContext是跟Windows无关的。因此新建Dialog的时候要用到Activity.this,否则会出错。

如何避免context引起的内存泄露:

·不要对activity的context长期引用(一个activity的引用的生存周期应该和activity的生命周期相同)

·如果可以的话,尽量使用关于application的context来替代和activity相关的context

·如果一个acitivity的非静态内部类的生命周期不受控制,那么避免使用它,正确的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRootImpl中内部类W所做的那样。

参考文章:
1、Android内存泄漏分析及调试

你可能感兴趣的:(android,内存,context)