Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anonymous Listener of volley request causing memory leak

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.

like image 669
Hardik Mehta Avatar asked Sep 22 '16 05:09

Hardik Mehta


People also ask

Which of the following should be avoided to prevent memory leak?

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.

How does memory leak debug and prevent it?

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.

When memory leak happens in Android?

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.


2 Answers

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.

like image 51
Bob Avatar answered Nov 09 '22 08:11

Bob


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.

like image 34
Nayan Srivastava Avatar answered Nov 09 '22 07:11

Nayan Srivastava