MonoDroid : Encoding.ASCII.GetString failing

2019-06-01 00:45发布

问题:

Intermittently, Encoding.ASCII.GetString call fails with an exception that escapes all the catch blocks in place and freezes the app.

private string ExecuteRequest(Uri url, KeyValuePair<string, string>[] postItems = null)
{
    var data = new byte[0];
    var response = new byte[0];
    using (var client = new WebClient())
    {
        if (postItems != null && postItems.Count() > 0)
        {
            string dataString = string.Join("&", postItems.Select(
                                    item => string.Format("{0}={1}", item.Key, item.Value)).ToArray());
            data = new ASCIIEncoding().GetBytes(dataString);
        }
        response = client.UploadData(url, "POST", data);
        Android.Util.Log.Info("info", "response from the post received. about to get string");
        client.Dispose();
    }
    try
    {
        return Encoding.ASCII.GetString(response);
    }
    catch (Exception ex)
    {
        Android.Util.Log.Info("info", 
            "Encoding.ASCII.GetString Exception : {0}, {1}", ex.Message, ex.StackTrace);
        throw new ApplicationException("UnRecoverable. Abort");
    }            
}

StackTrace I get is

I/sssvida (10960): response from the post received. about to get string
I/mono    (10960): Stacktrace:
I/mono    (10960):
I/mono    (10960):   at System.Text.ASCIIEncoding.GetString (byte[],int,int) <0x000cb>
I/mono    (10960):   at System.Text.Encoding.GetString (byte[]) <0x00037>
I/mono    (10960):   at ServiceRequest.ExecuteRequest (System.Uri,System.Collections.Generic.KeyValuePair`2<string, string>[]) <0x0026b>

Intermitently, I am getting an unusal stacktrace shown below

I/mono    ( 9817): Stacktrace:
I/mono    ( 9817):
F/        ( 9817): * Assertion at ../../../../mono/mini/mini-exceptions.c:472, condition `class' not met
D/dalvikvm(  220): GC_EXPLICIT freed 1K, 35% free 17547K/26759K, paused 3ms+3ms

The response is json data that can range between 1 - 4 mb.

Please help. Thanks!!!!

Edit 2 : I updated the code to use UploadString instead of UploadData and intermittently I get this :

I/mono    (15065): Stacktrace:
I/mono    (15065):
I/mono    (15065):   at string.CreateString (char[]) <0x0004b>
I/mono    (15065):   at (wrapper managed-to-managed) string..ctor (char[]) <0xffffffff>
I/mono    (15065):   at System.Text.Encoding.GetString (byte[],int,int) <0x00043>
I/mono    (15065):   at System.Text.UTF8Encoding.GetString (byte[],int,int) <0x0002b>
I/mono    (15065):   at System.Text.Encoding.GetString (byte[]) <0x00037>
I/mono    (15065):   at System.Net.WebClient.UploadString (System.Uri,string,string) <0x0007f>
I/mono    (15065):   at (wrapper remoting-invoke-with-check) System.Net.WebClient.UploadString (System.Uri,string,string) <0xffffffff>

回答1:

First, please fill a bug report as Rolf asked in the comments. The second stack trace is a crash that should never occur.

As for the first issue, is there any reason not to use (one of the overload) WebClient.UploadString ?

That method will take care of the encoding, ensuring the HTTP request header match, encoding the request and response... which will also return you a string (less code for you :-).

Otherwise you could end up with a string (unless you have other validations prior to this code) that cannot be encoded in ASCII and that would cause exceptions (sadly we only have the stack trace, not the exact exception being thrown to confirm this).



回答2:

Paging data and downloading in chunks might be the only correct and scalable/robust solution here.

For now, the code below is not blowing up, it may be that I have not hit the tipping point. But this is my first pass at chunking on the device. Encoding.ASCII.GetString does not blowing up in the code below.

private string ExecuteRequest(Uri url, KeyValuePair<string, string>[] postItems = null)
{
    var data = new byte[0];
    var response = new byte[0];
    ///switched to WebClient because
    /// http://stackoverflow.com/questions/8167726/monodroid-intermittent-failure-when-reading-a-large-json-string-from-web-servi
    using (var client = new WebClient())
    {
        if (postItems != null && postItems.Count() > 0)
        {
            string dataString = string.Join("&", postItems.Select(
                                    item => string.Format("{0}={1}", item.Key, item.Value)).ToArray());
            data = new ASCIIEncoding().GetBytes(dataString);
        }
        response = client.UploadData(url, "POST", data);
        Android.Util.Log.Info("info", "response from the post received. about to get string");
        client.Dispose();
    }
    try
    {
        Android.Util.Log.Info("info", "response size : {0}", response.Length);
        var chunkSize = 50000;
        if (response.Length > chunkSize)
        {
            var returnValue = new StringBuilder();
            for (int i = 0; i < response.Length; i+= chunkSize)
            {                        
                int end = (i + chunkSize) > response.Length ? response.Length - i : chunkSize;
                returnValue.Append(Encoding.ASCII.GetString(response, i, end));
                Android.Util.Log.Info("info", "added a chunk from {0} to {1}", i, end);
            }
            return returnValue.ToString();
        }
        return Encoding.ASCII.GetString(response);
    }
    catch (Exception ex)
    {
        Android.Util.Log.Info("info", 
            "Encoding.ASCII.GetString Exception : {0}, {1}", ex.Message, ex.StackTrace);
        throw new ApplicationException("UnRecoverable. Abort");
    }            
}