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.
Causes of Memory Leaks and Their Solutions One should not use static views while developing the application, as static views are never destroyed. One should never use the Context as static, because that context will be available through the life of the application, and will not be restricted to the particular activity.
Memory leak occurs when programmers create a memory in heap and forget to delete it. The consequences of memory leak is that it reduces the performance of the computer by reducing the amount of available memory.
Memory leaks occur when an application allocates memory for an object, but then fails to release the memory when the object is no longer being used. Over time, leaked memory accumulates and results in poor app performance and even crashes.
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 at onDestroy()
callback of Activity.
stringRequest.setTag(activityInstance);
To cancel all pending requests:
requestQueue.cancellAll(activityInstance);
Also, use Application context inside VolleySingleton to create the RequestQueue.
mRequestQueue = Volley.newRequestQueue(applicationContext);
Don't use your Activity context there and don't cache your Activity instance inside VolleySingleton.
Basically the anonymous approach is terrible in Android
or in any ClientSideSystem
where you don't have massive memory. What is happening is, you have passed Context
as parameter in method and anonymous
holds a reference of it. The real mess comes now in the scene when the thread inside which makes network call
could not finish it's job and before that the calling activity for some reason either destroys or recycles in that case GC
is not able to collect the activity as wokerThread
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 allow GC
to collect the object if no-one else if holding reference onto it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With