【问题标题】:MonoDroid : Intermittent failure when reading a large json string from web serviceMonoDroid:从 Web 服务读取大型 json 字符串时出现间歇性故障
【发布时间】:2011-11-17 13:17:43
【问题描述】:

我在线程上使用下面的代码(在 ui 中显示进度对话框)从 ASP.Net MVC Web 服务中读取 json 字符串。数据可以在 1 mb 到 4 mb 之间。

    public static class WebRequestEx
    {
        public static string ExecuteRequestReadToEnd(this WebRequest req)
        {
            var resp = req.GetResponse();
            using (var resps = resp.GetResponseStream())
            {
                StringBuilder sb = new StringBuilder();
                // read through the stream loading up the string builder
                using (var respRdr = new StreamReader(resps))
                {
                    //return respRdr.ReadToEnd();
                    while (!respRdr.EndOfStream)
                    {
                        sb.Append(respRdr.ReadLine());
                    }
                    return sb.ToString();
                }
            }
        }
}

堆栈跟踪如下。

I/mono    (15666): Stacktrace:
I/mono    (15666):
I/mono    (15666):   at System.Threading.WaitHandle.set_Handle (intptr) <0x0008b>
I/mono    (15666):   at System.Threading.EventWaitHandle..ctor (bool,System.Threading.EventResetMode) <0x00053>
I/mono    (15666):   at System.Threading.ManualResetEvent..ctor (bool) <0x0001f>
I/mono    (15666):   at (wrapper remoting-invoke-with-check) System.Threading.ManualResetEvent..ctor (bool) <0xffffffff>
I/mono    (15666):   at System.Net.WebAsyncResult.get_AsyncWaitHandle () <0x00073>
I/mono    (15666):   at System.Net.WebAsyncResult.WaitUntilComplete (int,bool) <0x00033>
I/mono    (15666):   at System.Net.WebConnectionStream.Read (byte[],int,int) <0x000b3>
I/mono    (15666):   at System.IO.StreamReader.ReadBuffer () <0x00047>
I/mono    (15666):   at System.IO.StreamReader.ReadLine () <0x0014b>
I/mono    (15666):   at System.WebRequestEx.ExecuteRequestReadToEnd (System.Net.WebRequest) <0x000ab>

问题是这种情况并非每次都发生,而且是间歇性的。这很糟糕,因为它会导致整个应用程序冻结并且进度对话框卡在用户无法对应用程序执行任何操作的情况下。我在调用代码中有一个 try/catch 块,但这个异常似乎可以解决。

我之前使用的是 ReadToEnd,但它也间歇性地爆炸,所以我切换到逐行阅读。

堆栈跟踪并不是特别有用,因为 Readline() 中似乎正在发生某些事情。

想法/建议/替代方案?

【问题讨论】:

  • 您应该考虑分页数据并分批带入,不建议在4mb大小的手机上一次下载较长时间。

标签: c# android multithreading mono xamarin.android


【解决方案1】:

除非您对WebRequest 有特殊要求,否则应使用WebClient

DownloadString 和(甚至更好)DownloadStringAsync 之类的方法将保护您免于处理响应位,因为这些响应位很容易分配给大量内存和影响应用程序性能的临时字符串。

【讨论】:

  • 从文档看来,WebClient 似乎只是简单地包装了 WebRequest 类。看看他们在那里做了哪些优化会很有趣吗?不再出现上述异常。暂时将此标记为答案。
  • 是的,它确实简单地包装WebRequest,但它正确地这样做了,如果不是最佳的或错误的,它将被优化/修复在较新的版本中,无需您更新代码。由于此代码直接来自 Mono(即它是开源的),因此您可以在 github 上阅读它 -> github.com/mono/mono/blob/mono-2-10/mcs/class/System/System.Net/…
  • poupou,谢谢。你以前碰到过这个吗? stackoverflow.com/questions/8216855/…
【解决方案2】:

尝试使用这个:

    public static string ExecuteRequestReadToEnd(this WebRequest req)
    {
        var resp = req.GetResponse();
        using (var resps = resp.GetResponseStream())
        {
            var buffer = new byte[16 * 1024];
            using (var ms = new MemoryStream())
            {
                int read;
                while ((read = resps.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                var content = ms.ToArray();
                return Encoding.UTF8.GetString(content, 0, content.Length);
            }
        }
    }

【讨论】:

    【解决方案3】:

    您需要增加 maxArrayLength 值,因为您返回的 byte[] 数组超过了 16348 的大小。您可以为 maxArrayLength 设置的最大值再次是最大 Int32 数,等于 2147483647。修改此设置,您的 Web 服务将能够在您的 Web 服务和客户端应用程序之间传输大数据。

    Web.config 中,生成客户端的 Web 服务引用绑定。

    “已超出传入消息的最大消息大小配额 (65536)。要增加配额,请使用相应绑定元素的 MaxReceivedMessageSize 属性。”

    将其设置为最大 2^31 - 1 或 2147483647 最大的 32 位数字。

    注意transferMode,默认是Buffered。

    • Bufferred 表示请求和响应消息都将被缓冲。

    transferMode的其他枚举:

    • 流式传输,这意味着请求和响应消息都将被流式传输,或者
    • StreamedRequest 请求将在响应消息缓冲时进行流式传输,或
    • StreamedResponse,在响应消息流式传输时将缓冲请求消息。

    对于那些不知道的人,缓冲意味着传输将把整个消息都保存在内存缓冲区中,直到传输完成。流式意味着只有消息头会被缓冲,而消息体将作为流公开。

    将 maxReceivedMessageSize 从 65536 更改为 2147483647 - 如果您离开 transferMode="Buffered" 我们可能会收到另一个错误:

    对于 TransferMode.Buffered,MaxReceivedMessageSize 和 MaxBufferSize 必须是相同的值。参数名称:绑定元素

    两个属性需要相同的值,如果整个消息被缓冲,MaxReceivedMessageSize 大于 MaxBufferSize。

    您有两个选择: a) 将 MaxBufferSize 更改为 2147483647 的大小 b) 将 transferMode 设置为:Streamed、StreamedRequest 或 StreamedResponse。

    “使用缓冲或流传输的决定是 HTTP 传输端点的本地决定。对于 HTTP 传输,传输模式不会通过连接传播,也不会传播到代理服务器或其他中介。设置传输模式未反映在服务合同的描述中。生成服务的代理后,您可以(允许但不是必需)编辑用于流传输的服务的配置文件以设置传输模式。对于 TCP和命名管道传输,传输模式作为策略断言传播。” http://msdn.microsoft.com/en-us/library/system.servicemodel.transfermode.aspx

    【讨论】:

      猜你喜欢
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-14
      • 1970-01-01
      • 2017-04-13
      相关资源
      最近更新 更多