Android HttpClient OOM на 4G/LTE (HTC Thunderbolt)
Я получил несколько сообщений от пользователей о сбоях, когда попробовал использовать мое приложение на Verizon 4G/LTE.
Если посмотреть на трассировку стека, то похоже, что реализация Android HttpClient.execute() выдает OOM. Это происходит только на устройствах 4G / LTE, в частности на HTC Thunderbolt, и только на устройствах 4G/LTE. WiFi, 3G, UMTS в порядке. Также отлично работает на WiMax 4G Sprint вещи отлично работает.
Два вопроса:
Какой лучший способ привлечь внимание разработчиков Android по этому поводу? Есть ли лучшие варианты, чем отчеты по http://code.google.com/p/android/issues?
Любые идеи о том, как я могу обойти это? У меня самого нет устройства 4G, и я не могу добиться этого в эмуляторе, поэтому мне нужно сделать некоторые догадки. Я могу попытаться поймать OOM в моем коде и попытаться очистить и заставить GC, но я не уверен, что это хорошая идея. Комментарии или другие предложения?
Вот что делает мой код:
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 );
...
Вот трассировка сбойного стека:
E / dalvikvm-heap (11639): Недостаточно памяти на выделении 2055696 байт. 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= дескриптор по умолчанию = 3948760 I / dalvikvm (11639): | schedstat = (208709711 74005130 214)
I / dalvikvm (11639): в org.apache.http.impl.io.AbstractSessionInputBuffer.init(AbstractSessionInputBuffer.java:~79) I/dalvikvm(11639): в org.apache.http.impl.io.SocketInputBu SocketInputBuffer.java:93) I/dalvikvm(11639): по адресу org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83) I/dalvik.ml (в кн.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170) I/dalvikvm(11639): по адресу org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:10ap.v.vv.jpv.jpg) impl.conn.DefaultClientConnection.openCompleted (DefaultClientConnection.java:129) I / dalvikvm (11639): по адресу org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection (по умолчанию ClientConnectionOalv): (116)) org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) I/dalvikvm(11639): в org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) I/dalvikvm(11639): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) I/dalvikvm или (11639): http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) I/dalvikvm(11639): в org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java: 116) 116: at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) I/dalvikvm(11639): at com.myapplication.Fetcher.trySourceFetch(Fetcher.java:205) I/dalvikv (: at com.myapplication.Fetcher.run(Fetcher.java:298) I/dalvikvm(11639): at java.lang.Thread.run(Thread.java:1102) I/dalvikvm(11639): E/dalvikvm(11639)): Недостаточно памяти: размер кучи =24171 КБ, выделенный =23142 КБ, размер растрового изображения =59 КБ, предел =21884 КБ E/dalvikvm(11639): Дополнительная информация: занимаемая площадь =24327 КБ, допустимый размер =24519 КБ, усеченная =348 КБ Вт / 11639 (): threadid=9: поток завершается с необработанным исключением (группа =0x40025b38)
3 ответа
Если посмотреть на трассировку стека, то похоже, что реализация Android HttpClient.execute() выдает OOM.
Это не указано в трассировке стека, имеющейся у вас в проблеме. Конечно, вы не предоставили всю трассировку стека по этой проблеме.
Какой лучший способ привлечь внимание разработчиков Android по этому поводу? Есть ли лучшие варианты, чем отчеты по http://code.google.com/p/android/issues?
Шансы на то, что это чистая ошибка Android, невелики, но не равны нулю.
Вот некоторые другие возможности, без определенного порядка:
Там нет проблем с
execute()
само по себе, но у вас просто не хватает памяти, а обнаруженные вами следы стека просто демонстрируют, чтоexecute()
подчеркивает вашу кучу.Проблема заключается в некоторых модификациях, которые HTC внесла в Android для Thunderbolt, возможно, вступив в силу только в сети LTE.
Эта проблема каким-то образом вызвана самой сетью Verizon LTE (например, некоторые их прокси-серверы отправляют информацию о бредовых шутках, что приводит к тому, что HttpClient имеет недовольство).
Любые идеи о том, как я могу обойти это?
Во-первых, я бы использовал существующие инструменты (например, выгрузку HPROF и проверку с помощью Eclipse MAT), чтобы подтвердить, что у вас нет утечки памяти в целом, что комбинация Thunderbolt/LTE, похоже, просто сработала.
Далее, я рекомендую вам найти способ последовательно воспроизвести ошибку. Это может быть ваше существующее приложение с серией шагов, которым нужно следовать, или это может быть выделенное приложение (например, зарегистрировать URL, который вызывает OOM, а затем создать крошечное приложение, которое просто выполняет этот запрос HttpClient). Я бы хотел, чтобы у DeviceAnywhere был Thunderbolt, но это не похоже на это. Я выведу несколько пробников и посмотрю, смогу ли я получить помощь на этом фронте.
С точки зрения обхода этого, в качестве временного промежутка, вы можете обнаружить, что вы работаете на Thunderbolt через android.os.Build
данные, и, возможно, что вы на LTE через ConnectivityManager
(Я предполагаю, что LTE будет указан как WiMAX, но это всего лишь предположение) и предупредит пользователей о проблемах с этим комбо.
Кроме того, вы можете попробовать немного изменить использование HttpClient и посмотреть, имеет ли он эффект, например:
- Если вы поддерживаете только API уровня 8 или выше, вы можете дать
AndroidHttpClient
выстрел в качестве замены замены - Отключить многопоточный доступ (в целом или для Thunderbolt) и избавиться от
ThreadSafeClientConnManager
Мне жаль, что у меня нет ответа "Волшебная пуля" для вас здесь.
ОБНОВИТЬ
Теперь, когда у меня есть полная трассировка стека, просмотр исходного кода... немного освещает.
Проблема заключается в том, что:
HttpConnectionParams.getSocketBufferSize(params);
возвращает значение 2MB или около того, которое вызывает OOM. Это очень большой буфер, особенно для движка Dalvik GC, который может быть фрагментирован (да, это слово снова).
params
здесь HttpParams
, Вы, кажется, создаете их сами через getHttpParams()
, Например, AndroidHttpClient
устанавливает это на 8192:
HttpConnectionParams.setSocketBufferSize(params, 8192);
Если вы сами устанавливаете размер буфера сокета, попробуйте уменьшить его. Если нет, попробуйте установить его на 8192 и посмотреть, поможет ли это.
Вот исправление: https://review.source.android.com/22852
в то же время, URLConnection является иммунитетом. только HttpClient имеет эту проблему.
если вы разработчик, желающий протестировать такого рода ошибки, вы можете использовать "adb shell setprop", чтобы установить, скажем, "net.tcp.buffersize.wifi", чтобы максимальные размеры буфера сокета для чтения / записи были огромны, когда ваш Устройство находится на Wi-Fi. что-то вроде следующего было бы настоящим стресс-тестом:
adb shell setprop net.tcp.buffersize.wifi 4096,80999999,80999999,4096,80999999,80999999
именно такое изменение конфигурации приводит к ошибке HttpClient. Я не знаю, каковы точные значения Thunderbolt, но кто-то с устройством может узнать, используя "adb shell getprop | grep buffersize".
Может быть, это поможет:
// 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);