Подтвердить что ты не робот

Android HttpClient OOM на 4G/LTE (HTC Thunderbolt)

Я получил несколько сообщений от пользователей об авариях при попытке использовать мое приложение на Verizon 4G/LTE.

Глядя на трассировку стека, похоже, что реализация Android HttpClient.execute() бросает OOM. Это происходит только на устройствах 4G/LTE, в частности на HTC Thunderbolt, и только на 4G/LTE. WiFi, 3G, UMTS в порядке. Также отлично работает на Sprint WiMax 4G, отлично работает.

Два вопроса:

  • Какой лучший способ привлечь внимание разработчиков 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 = default handle = 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.SocketInputBuffer. (SocketInputBuffer.java:93) I/dalvikvm (11639): при org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83) I/dalvikvm (11639): при org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170) I/dalvikvm (11639): при org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106) I/dalvikvm (11639): при org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129) I/dalvikvm (11639): при org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.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.execute(DefaultRequestDirector.java:348) I/dalvikvm (11639): при org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) I/dalvikvm (11639): при org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) I/dalvikvm (11639): при org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) I/dalvikvm (11639): при com.myapplication.Fetcher.trySourceFetch(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 = 24327KB, разрешено Footprint = 24519KB, Trimmed = 348KB W/dalvikvm (11639): threadid = 9: thread выход с неперехваченным исключением (Группа = 0x40025b38)

4b9b3361

Ответ 1

Глядя на трассировку стека, похоже, что реализация 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-specific) и избавиться от ThreadSafeClientConnManager

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


UPDATE

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

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

HttpConnectionParams.getSocketBufferSize(params);

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

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

HttpConnectionParams.setSocketBufferSize(params, 8192);

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

Ответ 2

здесь исправление: 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

Возможно, это поможет:

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