Android HttpClient OOM на 4G / LTE (HTC Thunderbolt) - PullRequest
21 голосов
/ 19 марта 2011

Я получил несколько сообщений от пользователей о сбоях, когда попробовал использовать мое приложение на 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 (116)org.)./ dalvikvm (11639): в org.apache.http.impl.conn.DefaultClientConnection.openCompleted (DefaultClientConnection.java:129)..java: 173) I / dalvikvm (11639): в 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): в org.apache.http.impl.client.DefaultRequestDirector.javateire по умолчанию (348) I / dalvikvm (11639): в org.apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.java:555) I / dalvikvm (11639): в org.apache.http.impl.client.Centent.execute (AbstractHttpClient.java:487) I / dalvikvm (11639): в org.apache.http.impl.client.AbstractHttpClient.execute (AbstractHttpClient.java:465) I / dalvikvm (11639): в com.myapplication.FtrySourceFetch (Fetcher.java:205) I / dalvikvm (11639): в com.myapplication.Fetcher.run (Fetcher.java:298) I / dalvikvm (11639): в java.lang.Thread.run (Thread.java:1102) I / dalvikvm (11639): E / dalvikvm (11639): Недостаточно памяти: Размер кучи = 24171 КБ, Выделено = 23142 КБ, Размер растрового изображения = 59 КБ, Предел = 21884 КБ E / dalvikvm (11639): Дополнительная информация: Footprint = 24327 КБ, Допустимый размер = 24519 КБ, обрезка = 348 КБ Вт / далвиквм (11639): нить = 9: нитьс необработанным исключением (группа = 0x40025b38)

Ответы [ 3 ]

26 голосов
/ 19 марта 2011

Глядя на трассировку стека, похоже, что реализация Android HttpClient.execute () выдает OOM.

Это не указано в трассировке стека, имеющейся у вас в проблеме. Конечно, вы не предоставили всю трассировку стека по этой проблеме.

Какой лучший способ привлечь внимание разработчиков Android по этому поводу? Любые лучшие варианты, чем отчетность по http://code.google.com/p/android/issues?

Вероятность того, что эта ошибка будет чистой для Android, невелика, но не равна нулю.

Вот некоторые другие возможности в произвольном порядке:

  1. Нет проблем с execute() как таковыми, но у вас просто не хватает памяти, и следы стека, с которыми вы столкнулись, просто демонстрируют, что execute() нагружает вашу кучу.

  2. Проблема заключается в некоторых модификациях, которые HTC внесла в Android для Thunderbolt, возможно, вступают в силу только в сети LTE.

  3. Эта проблема каким-то образом вызвана самой сетью 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

Извините, что у меня нет ответа "волшебная пуля" для вас здесь.


UPDATE

Теперь, когда у меня есть полная трассировка стека, просмотр исходного кода ... немного освещает.

Проблема заключается в том, что:

HttpConnectionParams.getSocketBufferSize(params);

возвращает значение 2 МБ, которое вызывает OOM. Это очень большой буфер, особенно для движка Dalvik GC, который может быть фрагментирован (да, это слово снова).

params вот это HttpParams. Вы, кажется, создаете их сами с помощью getHttpParams(). Например, AndroidHttpClient устанавливает значение 8192:

HttpConnectionParams.setSocketBufferSize(params, 8192);

Если вы сами устанавливаете размер буфера сокета, попробуйте уменьшить его. Если нет, попробуйте установить его на 8192 и посмотреть, поможет ли это.

4 голосов
/ 14 мая 2011

вот исправление: 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».

3 голосов
/ 08 июня 2011

Может быть, это поможет:

// 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);
...