Hot questions for Handling IllegalStateException in Retrofit

Top 10 Java Open Source / Retrofit / Handling IllegalStateException

java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2

Question: I am using Retro Fit to connect to API online. But I am getting this error while trying to parse the returned data.

retrofit.RetrofitError: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2

The data being returned is in This format and model for data is also given below:

iGPlaceApi.getStreams(ITEMS_PER_PAGE, pageNumber * ITEMS_PER_PAGE, new Callback<List<mGooglePlacesApiResponse>>() {

            @Override
            public void success(List<mGooglePlacesApiResponse> mGp, Response response) {
                int n = mGp.size();
                Object asa = mGp.toArray();
            }

            @Override
            public void failure(RetrofitError retrofitError) {
                String error = retrofitError.toString();
            }
        });

public class mGooglePlacesApiResponse {

    public String html_attributions;
    //public List<String> html_attributions;
    public String next_page_token;
    public List<place> results;
}

public class place {

    public Geometry geometry;
    public String icon;
    public String id;
    public String name;
    public OpeningHours opening_hours;
    public List<Photo> photo
    ...

Answer: Change List<mGooglePlacesApiResponse> mGp to mGooglePlacesApiResponse mGp. Your JSON contains object not list.


Retrofit "IllegalStateException: Already executed"

Question: I have a Retrofit network call that id like to run every 5 seconds. My current code:

Handler h = new Handler();
int delay = 5000; //milliseconds

h.postDelayed(new Runnable() {
    public void run() {
        call.enqueue(new Callback<ApiResponse>() {
            @Override
            public void onResponse(Response<ApiResponse> response) {
                Log.d("api", "response: " + response.body().getPosition().getLatitude().toString());
            }

            @Override
            public void onFailure(Throwable t) {

            }
        });
        h.postDelayed(this, delay);
    }
}, delay);

This runs once, but then throws the following:

java.lang.IllegalStateException: Already executed. at retrofit2.OkHttpCall.enqueue(OkHttpCall.java:52) at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.enqueue(ExecutorCallAdapterFactory.java:57) at orbyt.project.MyFragment$1.run(MyFragment.java:93)

Whats the issue here?

As a bonus: whats a better way to handle this? Ill be updating a map every update. I was thinking about trying to use Rx but not sure if this is an appropriate use-case, or how to implement it.

Answer: A Call can only be used once. Its documentation tells you how to use one multiple times:

Use clone() to make multiple calls with the same parameters to the same webserver; this may be used to implement polling or to retry a failed call.

So use call.clone().enqueue(..) for Asynchornous and call.clone().execute() for Synchornous respectively to ensure that you have a fresh, unexecuted Call for each request.


Retrofit - java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT

Question: I am trying to parse my own JSON, but getting JSONSyntaxException, here is how my JSON looks:

{
    "type":"success",
    "value":[
        {
            "id":1,
            "title":"Title - 1",
         "name":{
            "first":"First - 1",
            "last":"Last - 1"
         },
            "hobbies":[
                "Writing Code - 1",
            "Listening Music - 1"
            ]
        },
       .....
    ]

Log says:

E/app.retrofit_chucknorries.MainActivity$2: ERROR: com.google.gson.JsonSyntaxException: 
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT 
at line 7 column 12 path $.value[0].name
01-21 12:41:52.156 28936-28936/app.retrofit_chucknorries 
W/System.err: retrofit.RetrofitError: com.google.gson.JsonSyntaxException: 
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT 
at line 7 column 12 path $.value[0].name

Where I am doing mistake ? I just made few small modifications as per my requirement and classes else everything almost same as in original code Value.java:

public class Value {

    @SerializedName("id")
    @Expose
    private Integer id;

    @SerializedName("title")
    @Expose
    private String title;

    @SerializedName("hobbies")
    @Expose
    private List<String> hobbies = new ArrayList<String>();

    @SerializedName("name")
    @Expose
    private List<Name> name = new ArrayList<Name>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public List<Name> getName() {
        return name;
    }

    public void setName(List<Name> name) {
        this.name = name;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Answer: In your Master.java class, your name is not an array!

private List<Name> name = new ArrayList<Name>();

Change to this instead and try:

private Name name;

Actually by seeing the logs of the exception you can tell this.


com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

Question: I'm getting the following error

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

There are some other solutions out there, but none have worked for me. Most seem to be talking about having to make Call<WordOfTheDay> into Call<List<WordOfTheDay>>, but since the json is not an array that does not help. I believe it's the JSON, but am not sure.

JSON

{"id":520687,"word":"imbricated","contentProvider":{"name":"wordnik","id":711},"note":"The word 'imbricated' comes from the Latin word imbricātus, covered with roof tiles, from imbrex, imbric-, roof tile, from imber, imbr-, rain.","publishDate":"2015-11-30T03:00:00.000+0000","examples":[{"url":"http://api.wordnik.com/v4/mid/ae14ef37c95d853a3ccc48d6590a9e1875a7e0920882657447b9dd4443c5d17553ca0c76787ecd04d0559596157a208c","text":"As it flows it takes the forms of sappy leaves or vines, making heaps of pulpy sprays a foot or more in depth, and resembling, as you look down on them, the laciniated lobed and imbricated thalluses of some lichens; or you are reminded of coral, of leopards 'paws or birds' feet, of brains or lungs or bowels, and excrements of all kinds.","title":"Walden, or Life in the woods","id":930392764},{"url":"http://amzn.to/1P1n9xM","text":"Pushing the door open, he noticed an inscription on the frame around the imbricated scales: Portae meae tantum regi.","title":"Paradiso, by José Lezama Lima","id":0},{"url":"http://amzn.to/1I938NP","text":"The Subaruns' epidermal scales shimmered like imbricated armour: biological photocells drinking scorching blue Pleiadean sunlight.","title":"Galactic North, Alastair Reynolds","id":0}],"definitions":[{"text":"Overlapping, like scales or roof-tiles; intertwined.","partOfSpeech":"adjective","source":"wiktionary"}]}

Retrofit 2.0

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(MainActivity.BASE_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    //Initial Async Network Call Test
    WordAPI apiService = retrofit.create(WordAPI.class);
    Call<WordOfTheDay> call = apiService.getWordOfTheDay();
    call.enqueue(new Callback<WordOfTheDay>() {

        @Override
        public void onResponse(Response<WordOfTheDay> response, Retrofit retrofit) {
            int statusCode = response.code();
            WordOfTheDay temp = response.body();
            Log.d("temp","t");
        }

        @Override
        public void onFailure(Throwable t) {
            t.printStackTrace();
        }
    });

Interface

public interface WordAPI {

@GET("/words.json/wordOfTheDay?api_key=??")
Call<WordOfTheDay> getWordOfTheDay();
}

Answer: May be you have invisible chars before first '{' ? Because error said that first element is string instead of object, it's may be if real json look like: @#${"id":...


Get base64 image using Retrofit error com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:

Question: I am trying to download an Image from a authenticated site. the site returns a base64 version of image. is this the right way to do it on retrofit? how do get the image and set to my image view.

    @GET("/img/avatars/{id}")
    public void getProfilePic(@Path("id") int id,
                          Callback<TypedByteArray> result);

i set my restadapter logging to full and the response value looks like this

���V3��Ωw���Tw5vT��>8u�`�jS�������#���%A���"Xw��Oq������G@]éG���f~A#lD)<���•

not the base64 string.

What I have tried

customResAdapter(ImageService.class).getProfilePic(id, new Callback<TypedByteArray>() {
            @Override
            public void success(TypedByteArray result, Response response) { 
                try {
                    byte[] decodedString = Base64.decode(result.getBytes(), Base64.DEFAULT);
                    mProfilePic.setImageBitmap(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length));

                } catch (Exception e) {
                    e.printStackTrace();

                } 
            }

            @Override
            public void failure(RetrofitError error) { 
            }
        });

i dont know if the following codes are right but currently i get this error message

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path

Answer: If you're using GsonConverter you will always end up in JsonParseException. You need to subclass GsonConverter and avoid JsonParseExceptions and proceed with handling the Response itself.

Validating Json response in your subclassed Converter will give you hint how to handle the deserialization. In other words, if is valid json you pipeline it to GsonConverter, otherwise decode it from Base64.

public class TypedByteArrayConverter extends GsonConverter {
    public TypedByteArrayConverter(Gson gson) {
        super(gson);
    }

    public TypedByteArrayConverter(Gson gson, String charset) {
        super(gson, charset);
    }

    @Override
    public Object fromBody(TypedInput body, Type type) throws ConversionException {
        //if you are trying to deserialize POJO from json, make a supercall, otherwise convert to TypedByteArray
        if(!type.getClass().isAssignableFrom(TypedByteArray.class)) {
           return super.fromBody(body, type);
        }else {
            try {
                long length = body.length();
                ByteString base64 = ByteString.read(body.in(), (int) length);
                return new TypedByteArray(body.mimeType(), base64.toByteArray());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

ByteString is from Okio package

However, image loading with Retrofit is rather strange idea.