在AsyncTask中的上下文环境

3

我目前试图将我的应用程序的授权代码放入一个外部类中,这样每当需要时我就不必重复自己并整理代码。

我遇到的问题是,我似乎无法从AsyncTask内部访问我的Session变量。我很想尝试自己解决问题以更好地理解情况,但另一个问题是,在LogCat中没有错误信息,所以我不知道出了什么问题!我的授权类如下:

public class AuthoriseMe extends AsyncTask<Object, Void, String> {

    public Context context;

    protected String doInBackground(Object... params) {

    Log.i(TAG, "I GOT HERE");
    SessionManager session = new SessionManager(context);
    Log.i(TAG, "I GOT PAST HERE");

        String authorized = "";
        HashMap<String, String> app = session.getAPPDetails();
        String appid = app.get(SessionManager.KEY_APPID);
        String secret = app.get(SessionManager.KEY_SECRET);
    }
}

我使用以下代码启动AsyncTask,在该代码中,另一个类正在调用authoriseMe()函数:
public void authoriseMe() {
    AuthoriseMe authorise = new AuthoriseMe();
    authorise.execute();
}

由于我已记录了此过程,因此正在调用authoriseMe(),但看起来问题出在"SessionManager session = new SessionManager(context);"这一行,如果我将该行删除,则我的逻辑中会出现"I GOT PAST HERE"。

我的SessionManager构造函数如下:

// Constructor
public SessionManager(Context context){
    this._context = context;
    pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
    editor = pref.edit();
}

从我所看到的情况来看,这似乎是异步任务中上下文的问题。有没有什么解决方法?有人有过这方面的经验吗?


你在哪里告诉你的授权使用哪个上下文?看起来你只是在类中声明了上下文,但从未给它赋值? - Marko Lazić
在AsyncTask中,上下文是在哪里初始化的? - Raghunandan
这有点是我的问题,一个外部异步任务中是否有上下文?通常我会在包含异步任务类中分配它,但这与该流程分离。 - Steve Vincent
你应该采用下面给出的答案,因为活动有上下文,所以你可以通过它。 - Marko Lazić
只是一点小提示 - 如果您的上下文是活动上下文,请确保使用WeakReference<Context>。 - Submersed
2个回答

3

我发现您并没有初始化context成员变量。只需添加正确的构造函数即可:

public AuthoriseMe(Context context) {
    this.context = context;
}

然后,不是使用


AuthoriseMe authorise = new AuthoriseMe();

进行

AuthoriseMe authorise = new AuthoriseMe(context);

然后,您就完成了。或者,您可以通过调用getApplication()getApplicationContext()(请务必阅读此问题)来获取上下文,这对于大多数情况应该足够。


1
非常感谢您先生!我仍在努力理解上下文,特别是背景线程。 - Steve Vincent
1
它会导致内存泄漏。 - Faizan Mubasher

1
正如其他人所说,问题在于您从未显式地初始化或设置context变量,因此其值是隐式初始化的值。 当您写下以下代码时:
public class AuthoriseMe extends AsyncTask<Object, Void, String> {
    public Context context;
    ...

这相当于:
public class AuthoriseMe extends AsyncTask<Object, Void, String> {
    public Context context = null;
    ...

(在创建一个新的 AuthoriseMe 对象之前,null 赋值会发生,就在它的构造函数执行之前) 所以你可以看到,在你当前的代码中它仍然是 null,因此出现了问题。
更重要的是,请记住:
1. Android 的 Context 是高度状态化的,并且具有生命周期,因此 - 你要尽可能地避免泄漏它们(在内存方面) - 如果你试图使用已经结束生命周期的 Context(即已被销毁),你很可能会遇到麻烦
2. AsyncTask 的生命周期与任何 Activity 或其他 Context 生命周期无关,即这里你的 AuthorizeMe 任务可能正在运行并尝试做一些事情,而它持有的 Context 可能已经被销毁(这将再次导致问题...),所以 - 你应该避免在 AsyncTask 中持有一个对 Context 的引用
所以在你的情况下,你可以在你的SessionManager中保留对SharedPreferences的引用,并且不要将任何Context引用作为成员变量保留(这是我的首选),或者你也可以确保你持有的Context引用是应用程序上下文(它的生命周期与应用程序本身相同,因此“不会有问题”泄漏)。以下是第一种解决方案的示例:
public static class AuthoriseMe extends AsyncTask<Object, Void, String> {
    private final SessionManager mSessionManager;

    public AuthoriseMe(Context context) {
        this.mSessionManager = new SessionManager(context);
    }

    protected String doInBackground(Object... params) {
        String authorized = "";
        HashMap<String, String> app = mSessionManager.getAPPDetails();
        ...
    }
}

public static class SessionManager {
    private final SharedPreferences mSharedPreferences;

    public SessionManager(Context context){
        mSharedPreferences = context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
        editor = mSharedPreferences.edit();  // I wouldn't do that here ; edit when you will really need to and commit afterwards
    }
}

SharedPreferences 的作用域与应用程序本身相同,因此保留对其的引用是可以的。 请注意,我将 AsyncTask 类设置为静态,这将防止无意中泄漏对其嵌套对象(最可能是一个 Activity)的引用。

PS:Java 通常不使用下划线前缀命名约定 ;)


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