【问题标题】:Using context without any static reference使用没有任何静态引用的上下文
【发布时间】:2016-12-31 21:57:39
【问题描述】:

我正在尝试从 Singleton 类访问应用程序资源(具体为字符串资源)。作为 Singleton,此类不能保存对 Context 对象的任何引用(以防止内存泄漏)。当我在网上寻找其他实现时,我遇到了这两个实现:

  1. 在 Application 类中创建一个静态上下文并在整个应用程序中使用它。
  2. 将上下文作为参数传递给需要它的方法。

我不想使用第一个,因为它还使用对 Context 对象的静态引用。我知道在android的Application类中静态拥有它是可以的,但它仍然看起来像一个hack。

第二个实现是无用的,因为我没有任何可以传递给单例的 someOtherMethod 的上下文实例。

所以我想出了以下实现,当我初始化单例实例时,我使我的单例抽象覆盖其上下文需要方法(例如,下面代码中的getString(int resId))。

我很想知道这是否会导致任何内存泄漏?

我在哪里对这种方法感到困惑:

--> Overridden getString 中对上下文的引用是最终的。我不确定这是否会导致内存泄漏。

    public abstract class SingletonClass{

    .
    .
    .

    private static SingletonClass sInstance;

    private SingletonClass(Context paramContext) {
        // constructor code
    }

    public static SingletonClass getInstance(final Context context) {
        if (sInstance == null) {
            sInstance = new SingletonClass(context){
                @Override
                public String getString(int resId) {
                    return context.getString(resId);
                }
            };
        }
        return sInstance;
    }

    public abstract String getString(int resId);

    .
    .
    .

    private void someOtherMethod(){
        //uses above getString()
    }

    }

【问题讨论】:

  • 您能否分享一下您是如何使用这些字符串的,更具体地说,为什么您在使用它们时没有Context?在大多数情况下,Activity / Service / BroadcastReceiver / SQLiteOpenHelper / etc. 会提供上下文,使你的单例有点不必要。
  • 我正在开发一款安卓游戏并在其中使用 Google Play 游戏服务。我想到了单例,因为我需要在整个应用程序中保留一个 GoogleApiClient。

标签: android design-patterns memory-leaks singleton


【解决方案1】:

您的方法确实存在内存泄漏。传递给getInstance 的第一个上下文永远不会被垃圾收集,因为您的匿名类拥有对它的引用。 (并且有一个对匿名类的静态引用)。例如,如果您调用 getInstance(Activity),该活动将保留在内存中,直到进程被杀死!

幸运的是,有一个非常简单的修复方法可以消除内存泄漏。您可以安全地保留应用程序上下文 (context.getApplicationContext),它基本上是应用程序生命周期的单例上下文。

public static SingletonClass getInstance(Context c) {
    if (sInstance == null) {
        sInstance = new SingletonClass(c.getApplicationContext());
    }
    return sInstance;
}

【讨论】:

    【解决方案2】:

    您可以依赖于活动生命周期,并要求活动在 onResume 方法中传递对您的单例对象的引用,并在 onPause 中对其进行清理。

    protected void onResume() {
      super.onResume();
      Singleton.getInstance().onResume(this);
    }
    
    protected void onPause() {
      super.onResume();
      Singleton.getInstance().onPause();
    }
    

    另外,您可以刷新Context 的实例并将其保存在WeakReference 中:

    class Singleton {
      private WeakReference<Context> mContext;
    
      private boolean hasContext() {
        return mContext != null && mContext.get() != null;
      }
    
      public static Singleton getInstance(Context c) {
         //do your singleton lazy
         if (!sInstance.hasInstance()) {
           sInstance.mContext = new WeakReference<>(c);
         }
         return sInstance;
      }
    }
    

    第二种情况可以参考完成活动,所以我不建议这样做。

    【讨论】:

    • Singleton 是做什么异步的。如果Context突然得到null,会报错吧?
    • @AnkitMundada 如果您的上下文为空,那么您与上下文无关。如果资源的上下文为空,您可以只返回空。
    • 我的意思是,在您的第一种方法中,当我清理 onResume() 中的上下文时,但如果它仍然被 Singleton 的异步调用使用,它不会抛出异常吗?如果它仍然被异步进程使用,你为什么说“你不再与上下文有任何关系”?
    • 然后使用锁定,以避免异常。这是你的责任——处理异步代码,这并不常见。
    猜你喜欢
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多