I am using volley library for making web-services call. I made a general class for making all web services call and making service call from there and made anonymous listener for successful and error response.
But when I use leak canary it is showing memory leak related to context. Below is my snippet of code:
public void sendRequest(final int url, final Context context, final ResponseListener responseListener, final Map<String, String> params) {
StringRequest stringRequest;
if (isNetworkAvailable(context)) {
stringRequest = new StringRequest(methodType, actualURL + appendUrl, new Listener<String>() {
@Override
public void onResponse(String response) {
dismissProgressDialog(context);
try {
(responseListener).onResponse(url, response);
} catch (JsonSyntaxException e) {
// Util.showToast(context, context.getResources().getString(R.string.error));
Crashlytics.logException(e);
}
}
}, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Util.showToast(context,context.getString(R.string.error));
dismissProgressDialog(context);
if (error instanceof NetworkError) {
Util.showToast(context, context.getResources().getString(R.string.network_error));
} else if (error instanceof NoConnectionError) {
Util.showToast(context, context.getResources().getString(R.string.server_error));
} else if (error instanceof TimeoutError) {
Util.showToast(context, context.getResources().getString(R.string.timeout_error));
} else {
Util.showToast(context, context.getResources().getString(R.string.default_error));
}
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return request.getHeaders(context, actualURL, false);
}
};
stringRequest.setRetryPolicy(new DefaultRetryPolicy(30000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
VolleySingleton.getInstance(context).addRequest(stringRequest);
} else {
Util.showToast(context, context.getString(R.string.internet_error_message));
}
}
And I created an interface named response listener for redirecting responses to activity or fragment. I made request as follows.
Request.getRequest().sendRequest(Request.SOME URL, SplashScreenActivity.this, SplashScreenActivity.this, new HashMap<String, String>());
But I am facing memory leak as:
In 2.1.1:31.
* activity.SplashScreenActivity has leaked:
* GC ROOT com.android.volley.NetworkDispatcher.<Java Local>
* references network.Request$5.mListener (anonymous subclass of com.android.volley.toolbox.StringRequest)
* references network.Request$3.val$responseListener (anonymous implementation of com.android.volley.Response$Listener)
* leaks activity.SplashScreenActivity instance
* Retaining: 1.2MB.
* Reference Key: b8e318ea-448c-454d-9698-6f2d1afede1e
* Device: samsung samsung SM-G355H kanas3gxx
* Android Version: 4.4.2 API: 19 LeakCanary: 1.4 6b04880
* Durations: watch=5052ms, gc=449ms, heap dump=2617ms, analysis=143058ms
Any idea to remove this leak any help is appreciated.
Generally, Anonymous classes have a strong reference to the enclosing class instance. In your case, that would be SplashScreenActivity. Now I guess, your
Activity
is finished before you get the response from your server through Volley. Since the listener has a strong reference to enclosing Activity, that Activity cannot be garbage collected until the Anonymous class is finished. What you should do is tag all the requests you are sending with the Activity instance, and cancel all the requests atonDestroy()
callback of Activity.To cancel all pending requests:
Also, use Application context inside VolleySingleton to create the RequestQueue.
Don't use your Activity context there and don't cache your Activity instance inside VolleySingleton.
I had a similar problem detected with LeakCanary where Volley's mListener was referencing my response listener, and my listener was referencing an ImageView, so it could update it with the downloaded image.
I made my response listener an inner class within the activity ..
.. and stopped and started the volley request queue inside onDestroy() in the activity ..
This has fixed the leak.
Basically the anonymous approach is terrible in
Android
or in anyClientSideSystem
where you don't have massive memory. What is happening is, you have passedContext
as parameter in method andanonymous
holds a reference of it. The real mess comes now in the scene when the thread inside which makesnetwork call
could not finish it's job and before that the calling activity for some reason either destroys or recycles in that caseGC
is not able to collect the activity aswokerThread
might still be holding reference onto it. Please go through this for detail description.The solution could be either static inner classes or independent classes, in both cases use
WeakReference
to hold resources and do a null check before using them.Advantage of
WeakReference
is it will allowGC
to collect the object if no-one else if holding reference onto it.I know I m a bit late to join the party, but few days back this problem did spoil my weekend. In order to figure out, I went on to research a bit which finally got the solution.
The issue lies in the last request object getting leaked in Network Dispatcher & Cache Dispatcher.
As you can see a new request object is created before it takes from the queue. This overcomes the problem of memory leak.
P.S: Don't use Volley from the Google repository as it is deprecated and has this bug since then. In order to use Volley, go for this :
https://github.com/mcxiaoke/android-volley
The above repository is free from any memory leaks whatsoever. Ciao.