21

当我尝试在 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=默认句柄=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): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) I/dalvikvm(11639): at 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): 超出内存:堆大小=24171KB,分配=23142KB,位图大小=59KB,限制=21884KB E/dalvikvm(11639):额外信息:足迹=24327KB,允许足迹=24519KB,修剪=348KB W/dalvikvm(11639):threadid= 9:线程以未捕获的异常退出(组=0x40025b38)线程以未捕获的异常退出 (group=0x40025b38)线程以未捕获的异常退出 (group=0x40025b38)

4

3 回答 3

26

查看堆栈跟踪,看起来 Android 的 HttpClient.execute() 实现正在引发 OOM。

您在该问题上的堆栈跟踪并未表明这一点。当然,您没有提供有关该问题的整个堆栈跟踪。

什么是引起 Android 开发者注意的最佳方式?有比报告http://code.google.com/p/android/issues更好的选择吗?

这是一个纯 Android 错误的可能性很小,尽管不是零。

以下是其他一些可能性,不分先后:

  1. 本身没有问题execute(),但是您只是内存不足,而您遇到的堆栈跟踪只是表明这对execute()您的堆造成了压力。

  2. 问题在于 HTC 为 Thunderbolt 对 Android 进行的一些修改,可能仅在 LTE 网络上生效。

  3. 这个问题是由Verizon LTE网络本身引起的(例如,他们的一些代理发回导致HttpClient有一个conniption的怪诞信息)。

关于如何解决这个问题的任何想法?

首先,我将使用现有工具(例如,转储 HPROF 并使用 Eclipse MAT 进行检查)来​​确认您通常没有内存泄漏,Thunderbolt/LTE 组合似乎只是绊倒了。

接下来,我建议您想出一些方法来始终如一地重现错误。这可能是您现有的应用程序,需要遵循一系列步骤,或者它可能是一个专用应用程序(例如,记录触发 OOM 的 URL,然后创建一个仅执行该 HttpClient 请求的小型应用程序)。我希望 DeviceAnywhere 有一个 Thunderbolt,但它看起来不像。我会试探一下,看看能不能在这方面得到一些帮助。

就解决它而言,作为权宜之计,您可以检测到您正在通过android.os.Build数据在 Thunderbolt 上运行,也许您正在通过 LTE运行ConnectivityManager(我猜 LTE 会列为 WiMAX,但这只是一个猜测),并警告用户该组合的问题。

除此之外,您可以尝试稍微改变一下您的 HttpClient 使用情况,看看它是否有效果,例如:

  • 如果您只支持 API Level 8 或更高,您可以AndroidHttpClient尝试作为替代品
  • 禁用多线程访问(一般或特定于 Thunderbolt)并摆脱ThreadSafeClientConnManager

很抱歉,我在这里没有“灵丹妙药”的答案。


更新

现在我有了完整的堆栈跟踪,查看源代码是……有点启发性。

问题似乎是:

HttpConnectionParams.getSocketBufferSize(params);

正在返回触发 OOM 的 2MB 左右的值。这是一个非常大的缓冲区,尤其是对于 Dalvik GC 引擎,它可能会变得支离破碎(是的,又是那个词)。

params这里是HttpParams。您似乎正在通过getHttpParams(). 例如,AndroidHttpClient将其设置为 8192:

HttpConnectionParams.setSocketBufferSize(params, 8192);

如果您自己设置套接字缓冲区大小,请尝试减小它。如果没有,请尝试将其设置为 8192,看看是否有帮助。

于 2011-03-18T22:09:56.580 回答
4

这是修复:https ://review.source.android.com/22852

同时,URLConnection 是免疫的。只有 HttpClient 有这个问题。

如果你是一个想要测试这种故障的开发人员,你可以使用“adb shell setprop”来设置,比如“net.tcp.buffersize.wifi”,这样当你的最大读/写套接字缓冲区大小是巨大的设备在 wifi 上。像下面这样的东西将是一个真正的压力测试:

adb shell setprop net.tcp.buffersize.wifi 4096,80999999,80999999,4096,80999999,80999999

正是这种配置更改导致了 HttpClient 错误。我不知道 Thunderbolt 上的确切值是什么,但是使用该设备的人可以使用“adb shell getprop | grep buffersize”找到。

于 2011-05-14T01:18:30.547 回答
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);
于 2011-06-08T10:35:29.367 回答