android अनियंत्रित वॉली अनुरोध के श्रोता के कारण स्मृति रिसाव




web-services memory-leaks (4)

लीक कनानारी के साथ मुझे इसी तरह की समस्या का पता चला था जहां वॉली के एमएलिसर मेरी प्रतिक्रिया श्रोता को संदर्भित कर रहा था, और मेरा श्रोता एक इमेजिव्यू को संदर्भित कर रहा था, इसलिए यह डाउनलोड की गई छवि के साथ इसे अपडेट कर सकता था।

मैंने अपनी प्रतिक्रिया श्रोता को गतिविधि के भीतर एक आंतरिक वर्ग बना दिया ..

private class MyVolleyResponseListener <T> implements com.android.volley.Response.Listener <Bitmap> {

        @Override
        public void onResponse(Bitmap bitmap) {
            thumbNailView.setImageBitmap(bitmap);
        }
}

.. और गतिविधि में डास्टोरय () के अंदर वॉली अनुरोध कतार बंद कर दिया और शुरू किया ..

requestQueue.stop();
requestQueue.start();

इसने रिसाव तय कर लिया है

मैं वेब सेवा कॉल करने के लिए वॉली पुस्तकालय का उपयोग कर रहा हूं। मैंने सभी वेब सेवाओं को कॉल करने और वहां से सेवा कॉल करने और सफल और त्रुटि प्रतिक्रिया के लिए अनाम सुनवाई करने के लिए एक सामान्य वर्ग बनाया।

लेकिन जब मैं रिसाव कैनरी का इस्तेमाल करता हूं तो यह संदर्भ से संबंधित स्मृति रिसाव दिखा रहा है। नीचे कोड का मेरा स्निपेट है:

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));
    }
}

और मैंने गतिविधि या खंड के उत्तरों को रीडायरेक्ट करने के लिए प्रतिसाद श्रोता नामित एक इंटरफ़ेस बनाया है। मैंने निम्नानुसार अनुरोध किया

Request.getRequest().sendRequest(Request.SOME URL, SplashScreenActivity.this, SplashScreenActivity.this, new HashMap<String, String>());

लेकिन मुझे स्मृति रिसाव का सामना करना पड़ रहा है:

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

इस रिसाव को हटाने के लिए कोई भी विचार किसी भी मदद की सराहना की जाती है।


मुझे पता है कि मैं पार्टी में शामिल होने के लिए थोड़ा देर हो चुकी थी, लेकिन कुछ दिनों पहले इस समस्या ने मेरा सप्ताहांत खराब कर दिया था। पता लगाने के लिए, मैं कुछ शोध करने गया जो अंत में समाधान मिला।

यह मुद्दा नेटवर्क डिस्पैचर और कैशे डिस्पैचर में लीक किए गए अंतिम अनुरोध ऑब्जेक्ट में है।

@Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Make a blocking call to initialize the cache.
        mCache.initialize();

        Request<?> request;
        while (true) {
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // Take a request from the queue.
                request = mCacheQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
            try {
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    mDelivery.postResponse(request, response);
                } else {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    final Request<?> finalRequest = request;
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(finalRequest);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
            }
        }

जैसा कि आप देख सकते हैं कि कतार से ले जाने से पहले एक नया अनुरोध ऑब्जेक्ट बनाया गया है। यह स्मृति रिसाव की समस्या पर काबू पाता है

पुनश्च: Google रिपॉजिटरी से वॉली का उपयोग न करें क्योंकि इसे नापसंद किया गया है और उसके बाद से यह बग है। वॉली का उपयोग करने के लिए, इसके लिए जाएं:

https://github.com/mcxiaoke/android-volley

उपरोक्त भंडार किसी भी स्मृति लीक से मुक्त है। Ciao।


आम तौर पर, बेनामी कक्षाएं संलग्न वर्ग उदाहरण के लिए एक मजबूत संदर्भ है। आपके मामले में, यह स्पलैशस्क्रीनएक्टिविटी होगी। अब मुझे लगता है, वॉली के जरिए आपके सर्वर से प्रतिक्रिया मिलने से पहले आपकी Activity समाप्त हो जाती है चूंकि श्रोता का क्रियाकलाप संलग्न करने का एक मजबूत संदर्भ है, इसलिए कि अज्ञात वर्ग समाप्त होने तक गतिविधि कचरा एकत्रित नहीं हो सकती। आपको क्या करना चाहिए, वे सभी अनुरोधों को टैग करना है जो आप गतिविधि के उदाहरण के साथ भेज रहे हैं, और onDestroy() कॉलबैक गतिविधि के सभी अनुरोधों को रद्द कर दें।

stringRequest.setTag(activityInstance);

सभी लंबित अनुरोध रद्द करने के लिए:

requestQueue.cancellAll(activityInstance);

इसके अलावा, अनुरोध संदर्भ बनाने के लिए वॉली सिसिंगटन के अंदर अनुप्रयोग संदर्भ का उपयोग करें।

mRequestQueue = Volley.newRequestQueue(applicationContext);

वहां अपनी गतिविधि के संदर्भ का उपयोग न करें और वॉली सिसिंगटन के अंदर अपनी गतिविधि का उदाहरण कैश न करें।


असल में Android या किसी ClientSideSystem में गुमनाम दृष्टिकोण भयानक है, जहां आपके पास बड़े पैमाने पर मेमोरी नहीं है। क्या हो रहा है, आपने विधि में पैरामीटर के रूप में Context पारित किया है और anonymous में एक संदर्भ रखता है। असली गड़बड़ अब इस दृश्य में आता है जब धागे के भीतर जो network call करता है उसे नौकरी खत्म नहीं कर सका और इससे पहले कि कुछ कारणों से कॉलिंग गतिविधि या तो नष्ट हो जाती है या उस स्थिति में पुन: उपयोग की जाती है, GC गतिविधि को wokerThread रूप में एकत्र करने में सक्षम नहीं wokerThread अब भी हो सकता है उस पर संदर्भ धारण करना कृपया विस्तार विवरण के लिए इस माध्यम से जाएं।

समाधान या तो स्थैतिक आंतरिक कक्षाएं या स्वतंत्र वर्ग हो सकते हैं, दोनों ही मामलों में संसाधनों को पकड़ने के लिए WeakReference का उपयोग करते हैं और उनका उपयोग करने से पहले एक निरर्थक जांच करते हैं।

WeakReference का लाभ यह है कि GC ऑब्जेक्ट को इकट्ठा करने की इजाजत दे सकता है अगर कोई और नहीं तो उस पर संदर्भ रखेगा।





leakcanary