【问题标题】:Singleton with context in library (aar)库中带有上下文的单例 (aar)
【发布时间】:2017-03-02 10:52:28
【问题描述】:

我有一个需要上下文应用程序的库 (aar),因为它使用了一些需要上下文的 API 方法(例如WifiManager)。

我在there 和许多其他主题中看到,实现此目的的常用方法是将上下文作为参数传递给需要它的库对象。 我在阅读this 文章时遇到了问题。该库将由外部客户端使用,因此我想不惜一切代价避免所有空指针异常(您知道规则:如果它可以发生,它会在某个时候发生)。

假设我的库中有一个主入口点,这个类是一个 Singleton 并保存上下文(在 ctor 中或通过 setter)。我希望这个类“托管” Context 并且行为有点像 Application 类:它将为我的库的其他类提供上下文。

如何按照 nfrolov 的文章中描述的原则从“子”类或其他类中访问这个单例类?

让我们看一些(伪)代码,包括 MainSingletonNetworkManager(也是单例)和 DataProvider 类。

DataProvider 可以是MainSingleton 的成员并且不需要上下文,但NetworkManager 需要上下文才能使用WIFI_SERVICEregisterReceiver 方法。

然而,DataProvider 需要来自NetworkManager 的信息,要么通过注册一个监听器,要么从MainSingleton 获取数据(就像事件总线一样用于协调子类之间的事情,就像我们可以处理活动和多个片段)。

案例一

public class MainSingleton
{
    private static MainSingleton instance = new MainSingleton();
    private static Context context = App.getAppContext(); // where the frog do I get that from ?

    ...
}

问题:由于这是主要入口点,我无法访问此处的“App”,因此它应该作为参数传递给某处,作为设置器。但是随后上下文被初始化为 null ?

案例 2

public class MainSingleton {
    private static MainSingleton instance = null;

    private Context context;

    private MainSingleton(Context context) {
        this.context = context;
    }

    public synchronized static MainSingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MainSingleton(context.getApplicationContext());
        }

        return instance;
    }
}

问题:每个需要调用 MainSingleton 类的“子”实例也需要一个上下文。

编辑

阅读this 帖子后:我很可能会认为,当嵌入我的 aar 的客户端应用程序正在运行时,我的 MainEntryPoint 永远不会被卸载,将 Context 作为参数传递给 init 方法并保持静态。

【问题讨论】:

    标签: android singleton aar


    【解决方案1】:
    public class ConsumerManager {
    
       private static final ConsumerManager CONSUMER_MANAGER = new ConsumerManager();
       private Context context;
    
       public ConsumerManager() {
       }
    
       public static ConsumerManager getConsumerManager() {
           return CONSUMER_MANAGER;
       }
    
       public synchronized void initialize(Context context) {
           this.context= context;
       }
    
       public boolean isNetworkAvailable() {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
       }
    }
    

    首先你需要从活动中调用它。

    ConsumerManager.getConsumerManager.initialize(this);
    

    从您只需拨打电话的任何地方检查网络可用性

    ConsumerManager.getConsumerManager.isNetworkAvailable();
    

    [已编辑] 不要将 Android 上下文类放在静态字段中(对具有指向 Context 的字段活动的 ConsumerManager 的静态引用);这是内存泄漏(也会破坏 Instant Run)

    【讨论】:

    • 谢谢,但这并不能解决我的问题:“null static”案例。当客户端应用程序处于后台并且 Android 开始卸载类时,请参阅我提到的 nfrolov 的文章。
    • 是的,应用在后台时可能会出现问题。不要将 Android 上下文类放在静态字段中(对具有指向 Context 的字段活动的 ConsumerManager 的静态引用);这是内存泄漏(也会破坏 Instant Run)
    • 那么实际上如果传递的上下文是应用程序上下文,则没有内存泄漏,因为应用程序时库将被卸载,不是吗?
    • 如果您没有运行任何后台任务,如果活动被暂停或停止,系统可以通过要求它完成或简单地终止其进程来从内存中删除活动。如果您有一些长时间运行的 AsyncTask 或一个接一个地调用 Async Task。将会发生的是任务不会被破坏。假设您的任务之一是检查网络状态,但活动已经被破坏。在我的情况下,我正在显示进度对话框,它最终导致窗口泄漏。
    • 这就是为什么我指出我正在等待的上下文是上下文应用程序:)
    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多