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:
- Yii Framework y Aplicación para Android
- Cómo conectar la aplicación de Android a Amazon RDS
- Método correcto para llamar al servicio Web (API) de la clase Fragment
- Generar dirección desde Lat / Long en Android o REST
- ¿Por qué estoy recibiendo HttpResponse diferente de un navegador en android?
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.
- Servicio Web recibe parámetros nulos de la aplicación utilizando el método ksoap
- Llamadas de servicio síncronas en Android
- Serialice una matriz de ints para enviar usando KSOAP2
- Cómo obtener la imagen del servicio web a la aplicación Android
- Conexión de dos teléfonos Android para transferir datos entre ellos a través de WIFI
- Diferencia entre SOAP y KSOAP
- ¿Cual es mejor? Alto número de llamadas de servicio web (mensajes SOAP) o alta cantidad de datos en un único mensaje de jabón?
- App crash en el dispositivo HTC M8 con OS Lollipop para la llamada de servicio web
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.
- Consejos para optimizar el tiempo de construcción en el estudio de Android?
- La aplicación de conversión de Android se bloquea en convertir