如何从非活动类中检索上下文?

15

我发现了一个回答,该回答似乎说我应该创建一个单独的类并制作一个静态的MyApplication对象以及一个get方法。然后任何类都可以调用MyApplication.get()来检索上下文。

还有其他更简洁的方法吗?我的情况是这样的:

我有A类和B类。A类包含一个来自B类的对象(我们称之为b)。在A类中,我调用“b.play()”。但是,我得到了一个空指针异常,因为B类需要传递上下文给MediaPlayer.create()方法。

到目前为止,我组合了一种hack方法,从A类中调用.... "b.play(this)" 并将上下文简单地传递给B。然而,那相当丑陋,看起来像是糟糕的OOP使用方式。

有什么想法吗?


尽管这个问题已经较旧,但更好的讨论和更清晰的答案正在这个重复的问题上进行:https://dev59.com/OWsz5IYBdhLWcg3wxq4V - tir38
4个回答

13

这个问题在Android开发中似乎经常出现。获取特定上下文引用的一种解决方案是子类化Application并获取您想要的上下文引用。

public class MyApplication extends Application { 

private Context context;

@Override
public onCreate() {
  super.onCreate();
  this.context = getApplicationContext() // Grab the Context you want.
}

public static Context getApplicationContext() { return this.context; }
}

然而,这种解决方案需要您在清单文件中指定子类的名称。

<application
    android:name=".MyApplication"
</application>
你可以在应用程序的非活动类中像这样使用它,然后可以在应用程序的任何地方使用它。
MyApplication.getContext();  // Do something with the context! :)

你不能从静态函数返回实例变量。你可以将上下文变量重构为静态变量,但这可能会导致内存泄漏。 - winklerrr
这应该是可能的,因为引用的未初始化值只是 null,但否则变量应该像你说的那样被声明为静态的。 :) - Entalpi
3
由于其可能导致内存泄漏的潜在风险,应避免使用静态上下文。 - winklerrr
@winklerrr,你的解决方案是什么? - user1510006
1
@user1510006 通过构造函数将“Context”(例如您的活动或应用程序上下文)传递给非活动类。如果您需要确保使用应用程序上下文,请在构造函数内调用“context.getApplicationContext()”。 - winklerrr

11
如果B类需要Context来运行,那么通过play方法的参数、构造函数的参数等方式,让A类提供给它不会有任何问题。我认为,为B类提供执行其工作所需的依赖项,并不会导致OOP设计上的任何问题。

嗯,如果不被反对的话,我想我会保留它,但是将上下文传递给B类构造函数内部,以避免多次传递上下文。谢谢!到目前为止,这个上下文问题一直是我在Android开发中最复杂的部分~ - user427390
3
这实际上是非常好的面向对象编程,可以使测试更容易。依赖注入是一种常见的设计模式。 - jlindenbaum
将上下文传递给构造函数比在类中的每个方法中传递更可取,因为有时将上下文传递给类方法可能会导致NullPointerException。 - ChuongPham

1
传递“this”是一种可行的做法,特别是当“this”是创建需要上下文的对象的活动时。有时,我会将上下文放入构造函数中(例如“public MyObject(Context context){this.context = context;}”),这样您就不需要每次发送它。但是,如果您的对象在多个活动之间共享,则应该使用新活动更新它正在查看的上下文,尽管我还没有测试如果使用旧活动会发生什么。

4
我会把上下文放进构造函数中[然后保存它],但这样很容易导致内存泄漏。虽然我还没有测试如果使用旧活动会发生什么。几乎可以肯定的是会出现异常,并且此时您已经泄漏了上下文。 - Falmarri
1
那么通过方法传递更好吗?多次传递上下文所涉及的开销如何? - user427390
@Falmarri:啊,是的。当你打算关闭它时,你应该将上下文设置为null,以确保无法到达的闭合引用循环也可以进行垃圾回收。 - AlbeyAmakiir
@Google:将一个对象传递给另一个对象几乎没有任何额外开销。如果你的对象的生命周期与上下文的生命周期相关,那么传递它是可以的。但是请注意,现在你有可能会泄漏一个上下文。 - Falmarri
2
@AlbeAmakiir: “虽然无法访问的闭环引用是垃圾收集的候选对象”,但是垃圾回收器并不那么聪明。上下文是非常复杂的对象。尽管它们最终应该被垃圾回收,但由于上下文具有非常特定的生命周期,并且被保存在GC无法检查的各种位置中,因此它们很可能不会被垃圾回收。 - Falmarri
显示剩余4条评论

1

我也在这里回答过。

你可以使用ContextWrapper如此描述。

例如:

public class MyContextWrapper extends ContextWrapper {

    public MyContextWrapper(Context base) {
      super(base);
   }

   public void someMethod() {
      // MediaPlayer.create(this, ...)
   }

}

2
我该如何获得“MyContextWrapper”的实例? 我需要向它传递一个“Context”,这也是我一开始想要的。 - Suhas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接