Volley静态回调侦听器上下文GCed



使用LeakCanary后,我发现我的应用程序中有很多漏洞,其中大部分是由于Volley的匿名回调侦听器造成的。所以我写了一个Util(下面)类,它使用静态回调和WeakReference来保持对Context和匿名回调的引用。但当我第一次打开应用程序时,即冷启动时,在发出请求后不久,上下文就会被GCed,但在热启动期间,一切都很好。此外,这种情况只发生在应用程序中的第一个活动中。

也欢迎使用任何其他可以正常工作的方法来处理内存泄漏。

public abstract class VUtil {
    public static final String TAG = VUtil.class.getSimpleName();
    public interface JsonCallback {
        void onSuccess(JSONObject response);
    }
    public interface StringCallback {
        void onSuccess(String response);
    }
    public interface ErrorCallback {
        void onError(VolleyError error);
    }
    public static class JsonResponseListener implements Response.Listener<JSONObject> {
        private final WeakReference<Context> mContextWeakReference;
        private final WeakReference<JsonCallback> mCallbackWeakReference;
        public JsonResponseListener(Context context, JsonCallback callback) {
            mContextWeakReference = new WeakReference<>(context);
            mCallbackWeakReference = new WeakReference<>(callback);
        }
        @Override
        public void onResponse(JSONObject jsonObject) {
            Context context = mContextWeakReference.get();
            JsonCallback callback = mCallbackWeakReference.get();
            if (context != null && callback != null) {
                callback.onSuccess(jsonObject);
            } else {
                Log.d(TAG, "Context was GCed");
            }
        }
    }
    public static class StringResponseListener implements Response.Listener<String> {
        private final WeakReference<Context> mContextWeakReference;
        private final WeakReference<StringCallback> mCallbackWeakReference;
        public StringResponseListener(Context context, StringCallback callback) {
            mContextWeakReference = new WeakReference<>(context);
            mCallbackWeakReference = new WeakReference<>(callback);
        }
        @Override
        public void onResponse(String response) {
            Context context = mContextWeakReference.get();
            StringCallback callback = mCallbackWeakReference.get();
            if (context != null && callback != null) {
                callback.onSuccess(response);
            } else {
                Log.d(TAG, "Context was GCed");
            }
        }
    }
    public static class ErrorListener implements Response.ErrorListener {
        private final WeakReference<Context> mContextWeakReference;
        private final WeakReference<ErrorCallback> mCallbackWeakReference;
        public ErrorListener(Context context, ErrorCallback callback) {
            mContextWeakReference = new WeakReference<>(context);
            mCallbackWeakReference = new WeakReference<>(callback);
        }
        @Override
        public void onErrorResponse(VolleyError error) {
            Context context = mContextWeakReference.get();
            ErrorCallback callback = mCallbackWeakReference.get();
            if (context != null && callback != null) {
                callback.onError(error);
            } else {
                Log.d(TAG, "Context was GCed");
            }
        }
    }
}

GC依赖于许多正在发生的事情。这种情况的一个可能原因是,当你在"冷启动"后执行第一个请求时,你的应用程序必须初始化各种自定义对象、片段、活动、视图缓存等,因此在增加堆并进行GC之前需要内存。

然而,我建议的解决方案是更改您的体系结构。

1) 你似乎保留了上下文参考,但从未使用过。把放下

2) 你有Volley回调,它委托给你的自定义回调,你无论如何都需要传递,为什么不简单地使用一组回调,传递给相应的请求呢。

3) 你可以WeekRef你的自定义回调,但你不能没有它们。Week Referencing并不是内存泄漏的最终解决方案。你必须弄清楚为什么裁判在你不需要的时候还在那里。

所以,如果泄漏问题发生在JsonCallback、StringCallback和ErrorCallback实现中,请尝试解决这个问题,而不是延长链并在最后切割它。

感谢djodjo的回答,帮助我达成了解决方案

  • 始终使用addToRequestQueue(request, TAG)。这里的TAG位是当他们的活动/片段/视图或任何东西被GCed 时,我们将用来取消请求

  • 我所做的是创建一个基本活动,并在该活动中添加所有这些请求取消代码。这是的样子

    public abstract class BaseActivity extends AppCompatActivity {
        public final String tag;
        public BaseActivity() {
            super();
            tag = getClass().getSimpleName();
        }
        @Override
        protected void onDestroy() {
            App.getInstance().cancelRequests(tag);
            super.onDestroy();
        }
        protected <T> void addToRequestQueue(Request<T> request) {
            App.getInstance().addToRequestQueue(request, tag);
        }
    }
    

cancelRequests只是简单的代码

getRequestQueue().cancelAll(tag);

从此BaseActivity扩展您的活动,并使用addToRequestQueue进行请求,当您的活动被销毁时,请求将自动取消。对片段/对话框/其他任何内容执行类似的操作。

如果您从其他任何不遵循生命周期的地方发出请求,请确保它没有绑定到任何Context,这样您就可以了。

最新更新