Anónimo Oyente de la solicitud de volley causando pérdida de memoria

Estoy utilizando la biblioteca volley para hacer llamada de servicios web. Hice una clase general para hacer que todos los servicios web llamen y hagan llamadas de servicio desde allí y hagan oyentes anónimos para una respuesta exitosa y con errores.

Pero cuando uso leak canary está mostrando fuga de memoria relacionada con el contexto. A continuación se muestra mi fragmento de código:

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

Y he creado una interfaz llamada oyente de respuesta para redirigir las respuestas a la actividad o fragmento. Hice la solicitud como sigue.

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

Pero me enfrento a fugas de memoria como:

 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 

Cualquier idea para quitar esta fuga cualquier ayuda es apreciada.

Generalmente, las clases anónimas tienen una referencia fuerte a la instancia de clase que lo rodea. En su caso, que sería SplashScreenActivity. Ahora supongo que su Activity está terminada antes de recibir la respuesta de su servidor a través de Volley. Dado que el oyente tiene una fuerte referencia a incluir la actividad, esa actividad no se puede recolectar basura hasta que se termine la clase Anónimo. Lo que debe hacer es marcar todas las solicitudes que está enviando con la instancia de actividad y cancelar todas las solicitudes en la devolución de llamada onDestroy() de Actividad.

 stringRequest.setTag(activityInstance); 

Para cancelar todas las solicitudes pendientes:

 requestQueue.cancellAll(activityInstance); 

Además, utilice el contexto de la aplicación dentro de VolleySingleton para crear RequestQueue.

 mRequestQueue = Volley.newRequestQueue(applicationContext); 

No utilice su contexto de actividad y no almacene en caché su instancia de actividad dentro de VolleySingleton.

Básicamente, el enfoque anónimo es terrible en Android o en cualquier ClientSideSystem donde no tiene memoria masiva. Lo que está sucediendo es que has pasado Context como parámetro en el método y anonymous tiene una referencia de él. El lío real viene ahora en la escena cuando el hilo en el interior que hace network call no pudo terminar su trabajo y antes de que la actividad de llamada por alguna razón o destruye o recicla en ese caso GC no es capaz de recoger la actividad como wokerThread todavía puede ser Sosteniendo la referencia en él. Por favor, vaya a través de esto para la descripción de detalle.

La solución podría ser clases internas estáticas o clases independientes, en ambos casos usar WeakReference para retener recursos y hacer una comprobación nula antes de usarlos.

La ventaja de WeakReference es que permitirá GC para recoger el objeto si nadie más si la celebración de referencia sobre él.

Sé que soy un poco tarde para unirse a la fiesta, pero pocos días atrás este problema hizo estropear mi fin de semana. Con el fin de averiguar, me fui a la investigación un poco que finalmente consiguió la solución.

El problema radica en que el último objeto de solicitud se filtró en Network Dispatcher & Cache Dispatcher.

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

Como se puede ver, se crea un nuevo objeto de petición antes de que se tome de la cola. Esto supera el problema de la pérdida de memoria.

PD: No utilice Volley desde el repositorio de Google ya que está obsoleto y tiene este error desde entonces. Para usar Volley, vaya para esto:

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

El repositorio anterior está libre de cualquier pérdida de memoria en absoluto. Ciao.

  • Recurso IP en android
  • Mejores prácticas para la implementación del registro C2DM
  • Cómo llamar al servicio web https en Android
  • Android.os.NetworkOnMainThreadException para webservice (ksoap)
  • Cómo generar clases java desde el archivo WSDL
  • Servicio llamado de Android a C # no funciona
  • SOAP Envelope Response Error: Error al leer XMLStreamReader
  • Cómo llamar a un servicio web usando phonegap para android
  • Servidor REST Seguridad del servidor y seguridad del cliente android
  • Ajustar a las carreteras Android
  • El servidor no pudo procesar su solicitud. El valor no puede ser nulo: análisis de jabón
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.