Creating JSON with Gson Crashes the Application

I use Retrofit and Gson to upload a list of user objects to the server. I had no problems: test using Mororola, Asus and many other devices. Never a problem! Now I am working with a Zebra industrial smartphone, and I get my application almost always crashing during the creation of JSON (I recorded that the application was writing JSON before the crash). I check the data with TypeAdapter, so this should not be a data value problem. This is the log:

D/dalvikvm: JIT unchain all for threadid=13
W/dalvikvm: threadid=15: spin on suspend #1 threadid=13 (pcf=0)
W/dalvikvm: threadid=15: spin on suspend #2 threadid=13 (pcf=0)
I/dalvikvm: "Retrofit-Idle" prio=5 tid=15 RUNNABLE JIT
I/dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x41d52cc8 self=0x6065ec38
I/dalvikvm:   | sysTid=14452 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1599766536
I/dalvikvm:   | state=R schedstat=( 0 0 0 ) utm=9 stm=2 core=0
I/dalvikvm:     at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:~94)
I/dalvikvm:     at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:145)
I/dalvikvm:     at java.lang.StringBuffer.append(StringBuffer.java:219)
I/dalvikvm:     at java.io.StringWriter.write(StringWriter.java:167)
I/dalvikvm:     at com.google.gson.stream.JsonWriter.string(JsonWriter.java:570)
I/dalvikvm:     at com.google.gson.stream.JsonWriter.value(JsonWriter.java:419)
I/dalvikvm:     at my.company.app.rest.RestClient$1.write(RestClient.java:197)
I/dalvikvm:     at my.company.app.rest.RestClient$1.write(RestClient.java:165)
I/dalvikvm:     at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
I/dalvikvm:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113)
I/dalvikvm:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240)
I/dalvikvm:     at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
I/dalvikvm:     at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
I/dalvikvm:     at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
I/dalvikvm:     at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
I/dalvikvm:     at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
I/dalvikvm:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113)
I/dalvikvm:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240)
I/dalvikvm:     at com.google.gson.Gson.toJson(Gson.java:605)
I/dalvikvm:     at com.google.gson.Gson.toJson(Gson.java:584)
I/dalvikvm:     at com.google.gson.Gson.toJson(Gson.java:539)
I/dalvikvm:     at com.google.gson.Gson.toJson(Gson.java:519)
I/dalvikvm:     at retrofit.converter.GsonConverter.toBody(GsonConverter.java:80)
I/dalvikvm:     at retrofit.RequestBuilder.setArguments(RequestBuilder.java:375)
I/dalvikvm:     at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:298)
I/dalvikvm:     at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
I/dalvikvm:     at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:278)
I/dalvikvm:     at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
I/dalvikvm:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
I/dalvikvm:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
I/dalvikvm:     at retrofit.Platform$Android$2$1.run(Platform.java:142)
I/dalvikvm:     at java.lang.Thread.run(Thread.java:841)

I worked on it for several days without any specific result: could there be a memory problem? (I tested the application with older devices with less memory, and I had no problem.)

Many thanks!

UPDATE

, ? , json post call, while (boolean), , . , , dalvik "", ..... . qaru.site/questions/807637/...

+4
1

" " JVM. , , , HTTP- :

retrofit.RetrofitError: Java heap space
    at retrofit.RetrofitError.unexpectedError(RetrofitError.java:44)
    at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:400)
    at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
    at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:278)
    at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at retrofit.Platform$Base$2$1.run(Platform.java:94)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:421)
    at java.lang.StringBuffer.append(StringBuffer.java:272)
    at java.io.StringWriter.write(StringWriter.java:101)
    at com.google.gson.stream.JsonWriter.open(JsonWriter.java:327)
    at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:308)
    at q1.Q1$1$1.write(Q1.java:39)
    at com.google.gson.Gson.toJson(Gson.java:669)
    at com.google.gson.Gson.toJson(Gson.java:648)
    at com.google.gson.Gson.toJson(Gson.java:603)
    at com.google.gson.Gson.toJson(Gson.java:583)
    at retrofit.converter.GsonConverter.toBody(GsonConverter.java:80)
    at retrofit.RequestBuilder.setArguments(RequestBuilder.java:375)
    at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:298)
    ... 7 more

:

  • JVM: -Xms4m -Xmx8m
  • -: npm install http-echo-server -g && export PORT=8080 && http-echo-server ( . HTTP-, POST?)

? , , , StringWriter, ( , StringBuffer - ). :

interface IService {

    @POST("/")
    void query(@Body String s, Callback<String> callback);

}
    final int floodLength = 1024 * 1024;
    // This flooding type adapter factory substitutes any type, and this is OK for this case
    final TypeAdapterFactory flooder = new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
            return new TypeAdapter<T>() {
                @Override
                public void write(final JsonWriter out, final T value)
                        throws IOException {
                    // Just flood with `[{},{},{},...]`
                    out.beginArray();
                    for ( int i = 0; i < floodLength; i++ ) {
                        out.beginObject();
                        out.endObject();
                    }
                    out.endArray();
                }

                @Override
                @SuppressWarnings("unchecked")
                public T read(final JsonReader in)
                        throws IOException {
                    // Parse JSON response, however it does not work for this server giving war "POST "
                    // (the echo HTTP server dumps requests back as response bodies -- maybe another HTTP echo server is more appropriate for the experiment?)
                    loop:
                    for ( ; ; ) {
                        final JsonToken token = in.peek();
                        // @formatter:off
                        switch ( token ) {
                        case BEGIN_ARRAY: in.beginArray(); break;
                        case END_ARRAY: in.endArray(); break;
                        case BEGIN_OBJECT: in.beginObject(); break;
                        case END_OBJECT: in.endObject(); break;
                        case NAME: in.nextName(); break;
                        case STRING: in.nextString(); break;
                        case NUMBER: in.nextDouble(); break;
                        case BOOLEAN: in.nextBoolean(); break;
                        case NULL: in.nextNull(); break;
                        case END_DOCUMENT: break loop;
                        default: throw new AssertionError("no case for " + token);
                        }
                        // @formatter:on
                    }
                    return (T) "<mock>";
                }
            };
        }
    };
    final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(flooder) // registering the evil flooder for the testing purposes
            .create();
    final RestAdapter retrofit = new Builder()
            .setEndpoint("http://localhost:8080/")
            .setConverter(new GsonConverter(gson))
            .build();
    final IService service = retrofit.create(IService.class);
    service.query("foo", new Callback<String>() {
        @Override
        public void success(final String string, final Response response) {
            System.out.println("OK: " + string);
            System.exit(0);
        }

        @Override
        public void failure(final RetrofitError retrofitError) {
            retrofitError.printStackTrace(System.err);
            System.exit(1);
        }
    });

Writer , , , ( , , ). , , , - StringWriter - , - : , OutputStreamWriter OutputStream, , .. GsonConverter, Retrofit v1 ( Retrofit v2), , . , Gson Converter , :

final class StreamingGsonConverter
        implements Converter {

    private static final Charset charset = StandardCharsets.UTF_8;
    private static final String MIME_TYPE = "application/json; charset=" + charset;

    private final Gson gson;

    StreamingGsonConverter(final Gson gson) {
        this.gson = gson;
    }

    @Override
    public Object fromBody(final TypedInput body, final Type type)
            throws ConversionException {
        try {
            // Reading a stream can be much cheaper
            final Reader reader = new InputStreamReader(body.in(), charset);
            return gson.fromJson(reader, type);
            // No need to close the underlying InputStreamReader, let Retrofit do it
        } catch ( final IOException ex ) {
            throw new ConversionException(ex);
        }
    }

    @Override
    public TypedOutput toBody(final Object object) {
        return new BodyTypedOutput(object);
    }

    private final class BodyTypedOutput
            implements TypedOutput {

        private final Object object;

        private BodyTypedOutput(final Object object) {
            this.object = object;
        }

        @Override
        public String fileName() {
            return null;
        }

        @Override
        public String mimeType() {
            return MIME_TYPE;
        }

        @Override
        public long length() {
            // No one can know the length in advance for such cases...
            return -1;
        }

        @Override
        public void writeTo(final OutputStream out) {
            final Appendable appendable = new OutputStreamWriter(out, charset);
            // This is where the original GsonConverter fails for you: don't collect all the data in memory, but rather write it directly to the output stream
            gson.toJson(object, appendable);
            // No need to close the underlying OutputStreamWriter prematurely -- let Retrofit manage it itself because it the owner of the resource
        }
    }

}

GsonConverter :

.setConverter(new StreamingGsonConverter(gson))

JSON- (- , HTTP- POST), ( HTTP- - ). , , , . !

+1

Source: https://habr.com/ru/post/1668654/


All Articles