Android HttpClient OOM en 4G/LTE(HTC Thunderbolt)
htc-android htc-thunderbolt (3)
He recibido algunos informes de usuarios de bloqueos cuando intento usar mi aplicación en 4G / LTE de Verizon.
En cuanto al seguimiento de la pila, parece que la implementación de HttpClient.execute () de Android está lanzando un OOM. Esto sucede solo en dispositivos 4G / LTE, específicamente HTC Thunderbolt, y solo cuando está en 4G / LTE. WiFi, 3G, UMTS están bien. También funciona bien en cosas WiMax 4G de Sprint que funcionan bien.
Dos preguntas:
¿Cuál es la mejor manera de llamar la atención de los desarrolladores de Android sobre esto? ¿Alguna opción mejor que informar sobre http://code.google.com/p/android/issues ?
¿Alguna idea sobre cómo puedo solucionar esto? No tengo un dispositivo 4G y no puedo hacer que esto suceda en el emulador, así que necesito hacer algunas conjeturas aquí. Puedo intentar capturar el OOM en mi código e intentar limpiar y forzar el GC, pero no estoy seguro de que sea una buena idea. ¿Comentarios u otras sugerencias?
Esto es lo que hace mi código:
HttpParams params = this.getHttpParams(); // returns params
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, this.getHttpSchemeRegistry() );
DefaultHttpClient httpClient = new DefaultHttpClient( cm, params );
HttpResponse response = null;
request = new HttpGet( url );
try {
response = httpClient.execute(request); // <-- OOM on 4G/LTE. OK otherwise
int statusCode = response.getStatusLine().getStatusCode();
Log.i("fetcher", "execute returned, http status " + statusCode );
...
Aquí está el rastro de la pila que se estrella:
E / dalvikvm-heap (11639): sin memoria en una asignación de 2055696 bytes. I / dalvikvm (11639): "Thread-16" prio = 5 tid = 9 RUNNABLE I / dalvikvm (11639): | group = "main" sCount = 0 dsCount = 0 s = N obj = 0x48563070 self = 0x3c4340 I / dalvikvm (11639): | sysTid = 11682 nice = 0 sched = 0/0 cgrp = default handle = 3948760 I / dalvikvm (11639): | schedstat = (208709711 74005130 214)
I / dalvikvm (11639): en org.apache.http.impl.io.AbstractSessionInputBuffer.init (AbstractSessionInputBuffer.java:~79) I / dalvikvm (11639): en org.apache.http.impl.io.SocketInputBferfer. SocketInputBuffer.java:93) I / dalvikvm (11639): en org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer (SocketHttpClientConnection.java:83) I / dalvikvm (116.). DefaultClientConnection.createSessionInputBuffer (DefaultClientConnection.java:170) I / dalvikvm (11639): at org.apache.http.impl.SocketHttpClientConnectionvacio_vivé_v.v. impl.conn.DefaultClientConnection.openCompleted (DefaultClientConnection.java:129) I / dalvikvm (11639): at org.apache.http.impl.conn.DefaultClientConparticación de las partes de los animales / de los animales / de los animales / de la empresa: Víctor. org.apache.http.impl.conn.AbstractPoolEntry.open (AbstractPoolEntry.java:164) I / dalvikvm (11639): en org.apache.http.impl.conn.AbstractPooledC onnAdapter.open (AbstractPooledConnAdapter.java:119) I / dalvikvm (11639): en org.apache.http.impl.client.DefaultRequestDirector.execute (DefaultRequestDirector.java:348) I / dalvikvm (11639): at org. http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.java:555) I / dalvikvm (11639): at org.apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.java:487) I / dvvvvvv : en org.apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.java:465) I / dalvikvm (11639): en com.myapplication.Fetcher.trySourceFetch (Fetcher.java:205) I / dalvikvm (11639) : en com.myapplication.Fetcher.run (Fetcher.java:298) I / dalvikvm (11639): en java.lang.Thread.run (Thread.java:1102) I / dalvikvm (11639): E / dalvikvm (11639 ): Sin memoria: Tamaño del montón = 24171KB, Asignado = 23142KB, Tamaño del mapa de bits = 59KB, Límite = 21884KB E / dalvikvm (11639): Información adicional: Huella = 24327KB, Huella permitida = 24519KB, Recortado = 348KB W / dalvikvm ): threadid = 9: subproceso que sale con una excepción no detectada (grupo = 0x40025b38)
En cuanto al seguimiento de la pila, parece que la implementación de HttpClient.execute () de Android está lanzando un OOM.
Eso no está indicado por el seguimiento de pila que tiene sobre el problema. Por supuesto, no proporcionó todo el seguimiento de pila en el problema.
¿Cuál es la mejor manera de llamar la atención de los desarrolladores de Android sobre esto? ¿Alguna opción mejor que informar sobre http://code.google.com/p/android/issues ?
Las probabilidades de que esto sea un error puro de Android son pequeñas, aunque no nulas.
Aquí hay algunas otras posibilidades, sin ningún orden en particular:
No hay ningún problema con
execute()
per se, pero simplemente se está quedando sin memoria, y los rastros de pila que ha encontrado simplemente demuestran queexecute()
está acentuando su montón.El problema está en algunas modificaciones que HTC hizo a Android para el Thunderbolt, y posiblemente solo tendrá efecto cuando esté en la red LTE.
El problema se debe de alguna manera a la propia red LTE de Verizon (por ejemplo, algún proxy de ellos que envía información de screwball que está causando que HttpClient tenga una connipción).
¿Alguna idea sobre cómo puedo solucionar esto?
Primero, usaría las herramientas existentes (p. Ej., Descargando HPROF y examinando con Eclipse MAT) para confirmar que, en general, no hay una pérdida de memoria que parece que el combo Thunderbolt / LTE simplemente se está tropezando.
A continuación, te recomiendo que encuentres alguna forma de reproducir el error de manera consistente. Esa podría ser su aplicación existente con una serie de pasos a seguir, o podría ser una aplicación dedicada (por ejemplo, registrar la URL que activa el OOM, luego crear una pequeña aplicación que solo haga esa solicitud HttpClient). Desearía que DeviceAnywhere tuviera un Thunderbolt, pero no lo parece. Pondré algunos sensores y veré si puedo obtener ayuda en ese frente.
En términos de solucionarlo, como recurso provisional, puede detectar que se está ejecutando en un Thunderbolt a través de android.os.Build
datos, y tal vez que esté en LTE a través de ConnectivityManager
(supongo que LTE aparecería como WiMAX , pero eso es solo una conjetura), y advertir a los usuarios sobre los problemas con ese combo.
Más allá de eso, puedes intentar cambiar tu uso de HttpClient un poco y ver si tiene algún efecto, como:
- Si solo es compatible con el nivel de API 8 o superior, podría darle una oportunidad a
AndroidHttpClient
como reemplazoAndroidHttpClient
- Deshabilitar el acceso a múltiples subprocesos (en general o específicos de Thunderbolt) y deshacerse de
ThreadSafeClientConnManager
Lamento no tener una respuesta de "bala mágica" para usted aquí.
ACTUALIZAR
Ahora que tengo el seguimiento completo de la pila, mirar a través del código fuente es ... un poco iluminador.
El problema parece ser que:
HttpConnectionParams.getSocketBufferSize(params);
está devolviendo ese valor de aproximadamente 2MB que está activando el OOM. Es un búfer tremendamente grande, especialmente para el motor Dalvik GC, que puede fragmentarse (sí, hay otra palabra).
params
aquí es el HttpParams
. Parece que los estás creando tú mismo a través de getHttpParams()
. Por ejemplo, AndroidHttpClient
establece eso en 8192:
HttpConnectionParams.setSocketBufferSize(params, 8192);
Si está configurando el tamaño del búfer de socket, intente reducirlo. Si no, intente configurarlo en 8192 y vea si eso ayuda.
Aquí está la solución: https://review.source.android.com/22852
Mientras tanto, URLConnection es inmune. solo HttpClient tiene este problema.
Si eres un desarrollador que desea probar este tipo de falla, puedes usar "adb shell setprop" para establecer, digamos, "net.tcp.buffersize.wifi", de modo que los tamaños máximos de búfer de socket de lectura / escritura sean enormes cuando tu El dispositivo está en wifi. Algo como lo siguiente sería una prueba de estrés real:
adb shell setprop net.tcp.buffersize.wifi 4096,80999999,80999999,4096,80999999,80999999
es este tipo de cambio de configuración que ejerce el error HttpClient. No sé cuáles son los valores exactos en el Thunderbolt, pero alguien con el dispositivo podría descubrirlo utilizando "adb shell getprop | grep buffersize".
Tal vez esto ayude:
// Set the timeout in milliseconds until a connection is established.
int timeoutConnection = 5000;
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 4000;
// set timeout parameters for HttpClient
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
HttpConnectionParams.setSocketBufferSize(httpParameters, 8192);//setting setSocketBufferSize
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.setParams(httpParameters);