传递 Context 给非 Activity 类的最佳实践是什么?

58
所以,我几乎编码完成了我的第一个主要应用程序,并且正在对我的代码进行优化。应用程序运行良好,但是我不确定将上下文传递给其他类的方式是否正确。我不想走错路。我在Stackoverflow上偶然看到有关上下文及其正确传递方式的文章和问题。我也阅读了文档,但是作为芬兰人,复杂的技术术语更难理解。

所以,一个简单的问题。我传递主活动的上下文给其他(辅助)类的方式是否正确?如果不正确,在哪里可以了解更多关于这些情况的最佳实践。

例如: MainActivity.java

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle sis){
        super(sis);
        new Helper(MyActivity.this).makeMyAppAwesome();
    }
}

Helper.java

public class Helper {
    Context context;
    Helper(Context ctx){
        this.context = ctx;
    }

    public void makeMyAppAwesome(){
        makeBaconAndEggsWithMeltedCheese(context);
    }
}

这样可以吗?如果有人能提供一篇易于阅读的文章并附带示例,那就太好了。


5
基本的经验法则是不要在Context结束后再继续保留这个Context - Dan S
1
这是我不确定理解的其中一句话。很明显,我在另一个活动中没有使用相同的上下文,但这可能不是你所说的吧? - Iiro Krankka
3
对于你的例子,理解Activity生命周期是理解这句话的关键。这将防止内存泄漏和使用旧的Activity实例(在你的例子中不会发生这种情况)。在你的helper中,我会包含一个方法,在onStop中将activity设置为null,并在onStart中再次设置该值。 - Dan S
8
他的意思是你的 Helper 类可能比 MainActivity 存在时间更长。这意味着仍然有一个对它(即它的上下文)的引用,因此它不能被垃圾回收。这会导致内存泄漏。避免这种情况的最好方法是:不要存储上下文,使用参数代替。比如 new Helper().makeMyAppAwesome(ctx); 这样你就有一个持续时间和你需要它一样长的上下文的引用 - 但是当活动被销毁时,你不必担心清除所有存储的引用。 - user658042
谢谢。这让我清楚了很多。我现在明白了Activity生命周期并已经在onPause中注销了一些接收器和其他东西,但从未想到过这一点。所以,我可以通过每次MainActivity暂停时使Helper.context = null来实现这一点,然后在onResume中恢复它吗?或者就像alextsc建议的那样。 - Iiro Krankka
如果我的主Activity中有一个公共方法getContext(),并且每当我需要上下文时,我都从我的Helper中调用它,那么即使我的主Activity被暂停,其他类也会使用上下文吗? - Iiro Krankka
4个回答

43
您可以使用ContextWrapper 来实现此操作,具体请参见这里的描述。 例如:
public class MyContextWrapper extends ContextWrapper {

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

    public void makeMyAppAwesome(){
        makeBaconAndEggsWithMeltedCheese(this);
    }
}

从 Activity 中像这样调用非活动类:

new MyContextWrapper(this);

1
欢迎来到SO,赞赏你在第一篇帖子中提供了比已有的并被接受的流行问题更好的答案!我只是编辑了你的答案以正确格式化代码 - 下次回答时请查看帮助链接以获取更多信息。 :) - OJFord
这与保留上下文一样糟糕。请注意,ContextWrapper将保留对上下文的引用。如果您正在使用它,请确保正确取消引用所创建的ContextWrapper。 - Dave Thomas
@DaveThomas 我在应用程序级别中有一个帮助类。如果我使用这种方法并传递应用程序上下文,会导致内存泄漏吗? - Madhan
@Madhan 只需在需要时通过上下文传递即可,不要保留它。任何需要的函数都应将其作为参数接受。请参阅此处详细说明该方法的答案。 - Dave Thomas

4
通常最好在需要当前上下文的时候直接传递它。将其存储在成员变量中可能会导致内存泄漏,并在构建应用程序中的更多活动和服务时开始出现问题。
public void iNeedContext(Context context) {...

此外,在任何有上下文的类中,我建议创建一个成员变量以增加可读性和可搜索性,而不是直接传递this(ClassName.)this。例如在MainActivity.java中:
Context mContext = MainActivity.this;
Activity mActivity = MainActivity.this;

0
我已经传递了上下文,像这样解决了我的问题:
    public class Utils extends ContextWrapper {
private final Context context;
    
    public Utils(Context context) {
        super(context);
        this.context = context;
    }
public void mymethod(){}
}

super(context);ContextWrapper 结合使用,有助于使 getBaseContext()getApplicationContext() 有效,并且 this.context = context; 将上下文捕获到变量中,我可以在方法中随时使用。 或者,您可以选择使用带有 this.context = context; 的构造函数,并替换所有出现的 getApplicationContext()getBaseContext()。 更好的方法是,如果仅从类中使用几个,则直接将上下文传递给方法,以避免内存泄漏。


-4

你也可以在onCreate()方法中创建一个静态实例引用到你的MainActivity

public class MainActivity extends AppCompatActivity {

    public static MainActivity mMainActivity;

    @Override
    private onCreate(Bundle savedInstanceState){

    //...

    mMainActivity = this;

    }
}

并且像这样调用上下文:

MainActivity.mMainActivity;

或者编写一个方法getInstanceOf(),如果这样更清晰和/或您更喜欢使用访问器。
MainActivity.getInstanceOf();

如果您稍后决定要调用包含在主活动中的实例方法,那么这种策略可能会为您提供一些灵活性,例如:

MainActivity.mMainActivity.myInstanceMethod();

仅仅是一个建议。欢迎并鼓励批评。


3
公共的静态 MainActivity mMainActivity; 这将创建一个全局静态 Activity 实例,并且不会被垃圾回收,因此它会导致内存泄漏。 - Jay Donga

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