Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Volley Memory Leaks

I use google's volley library and I have been battling memory leaks in my apps for weaks now. I have done soo much research and tried soo much already but now I just do not know what to do. This is a sample code:

SplashActivity.java

public class SplashActivity extends AppCompatActivity {

    Context mContext;
    AuthRequest mAuthRequest;
    GetTokenOnSuccessListener mGetTokenOnSuccessListener;
    GetTokenOnErrorListener mGetTokenOnErrorListener;
    private ConfigTable mConfigTable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initialiseViewsAndComponents();
    }

    @Override
    protected void onStart() {
        super.onStart();
        getAuthToken();
    }

    private void initialiseViewsAndComponents() {
        mContext = SplashActivity.this;
        mAuthRequest = new AuthRequest(mContext);
        mGetTokenOnSuccessListener = new GetTokenOnSuccessListener(mContext);
        mGetTokenOnErrorListener = new GetTokenOnErrorListener(mContext);
        mConfigTable = new ConfigTable(mContext);
    }

    private void getAuthToken() {
        if (!mConfigTable.get("INITIALISED").equals("")) {
            mAuthRequest.guest(mGetTokenOnSuccessListener, mGetTokenOnErrorListener);
        } else {
            Intent mainIntent = new Intent(mContext, MainActivity.class);
            startActivity(mainIntent);
        }
    }

}

GetTokenOnSuccessListener.java

public class GetTokenOnSuccessListener implements Response.Listener<JSONObject> {

    //private Activity mActivity;
    private Context mContext;
    private ConfigTable mConfigTable;
    private int mSuccess = 0;
    private String mMessage = "";

    public GetTokenOnSuccessListener(Context context) {
        //this.mActivity = context;
        this.mContext = context;
        this.mConfigTable = new ConfigTable(this.mContext);
    }

    @Override
    public void onResponse(JSONObject response) {
        try {
            mSuccess = Integer.parseInt(response.get("success").toString());
            mMessage = response.get("message").toString();

            if (mSuccess == 1) {
                mConfigTable.setAuthToken(response.get("message").toString());
                Intent mainIntent = new Intent(mContext, MainActivity.class);
                mContext.startActivity(mainIntent);
                ((SplashActivity) mContext).finish();
            } else {
                Toast.makeText(mContext, "Lol access denied, could not retrieve token from server.", Toast.LENGTH_SHORT).show();
            }


        } catch (JSONException e) {
            e.printStackTrace();
            Toast.makeText(mContext, "Lol access denied, could not retrieve token from server.", Toast.LENGTH_SHORT).show();
        }
    }
}

GetTokenOnErrorListener.java

public class GetTokenOnErrorListener implements Response.ErrorListener {

    private Context mContext;

    public GetTokenOnErrorListener(Context context) {
        this.mContext = context;
    }

    @Override
    public void onErrorResponse(VolleyError error) {
        Utils.showNetworkResponse(mContext, error);
    }
}

Okay now I moved the response listeners to their own separate classes based on something I read online thinking it will resolve the leak but no it did not. I added code to cancel all pending requests onDestroy() based on the request's tag but still I had memory leaks.

This is just my splash activity and the leaks here are small, I have a feeling it's because I call finish() but I don't get that because I call it after the request has been completed successfully. All my other activities have similar codes but leak more memory as much as 11mb.

So my question is has anyone worked with the volley library? How do I use it and avoid memory leaks?

Using this version:

compile 'com.android.volley:volley:1.0.0'
like image 231
user3718908x100 Avatar asked Oct 30 '17 22:10

user3718908x100


2 Answers

It's not enough just to "Move response listeners to their own separate classes". Your listeners have strong references to the Activity (mContext), introducing a leak during the request. It means that your Activity can't be garbage collected, while the request is ongoing. It's not really a Volley's fault, but rather a natural way of things.

You have couple of options in your case:

1) Pass a WeakReference<Context> to your listeners, instead a strong reference to Context. This way you won't introduce a leak and will have to check if this referenced Context isn't yet null, when you try to access it. But I'd rather go for the 2nd option.

2) Set mContext to null in listeners, when Activity's onDestroy() is called. And perform null check as well, when you are trying to do something with Context in listener. So as soon as Activity will be destroyed, you'll remove other strong references to it, allowing GC to collect it normally.

like image 116
Dimezis Avatar answered Sep 27 '22 23:09

Dimezis


Please update to latest volley version they have fixed memory leaks. 'com.android.volley:volley:1.1.0-rc1'

https://github.com/google/volley/releases/tag/1.1.0-rc1

like image 32
dayanand Avatar answered Sep 28 '22 01:09

dayanand