Search code examples
javaandroidjsonapiretrofit

API doesn't load


So basically I tried to make a watchlist app, but It keeps crashing when I search for A title.

I'm using the JikanAPI with Retrofit, Please help me out because I'm trying to fix this in a week. I'm dying inside :)

here is the error log:

2020-01-21 20:46:32.150 5817-5946/r D/OkHttp: <-- 200 OK https://api.jikan.moe/v3/search/anime?q=Naruto (90ms)
2020-01-21 20:46:32.150 2583-5920/? I/PTCommittedOperation: Receive new configuration for com.google.android.gms.recaptcha
2020-01-21 20:46:32.150 5817-5946/r D/OkHttp: Server: nginx/1.14.0 (Ubuntu)
2020-01-21 20:46:32.151 5817-5946/r D/OkHttp: Date: Tue, 21 Jan 2020 19:46:28 GMT
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Content-Type: application/json
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Transfer-Encoding: chunked
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Connection: keep-alive
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Access-Control-Allow-Origin: *
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Access-Control-Allow-Methods: *
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Cache-Control: private, must-revalidate
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: ETag: "f2334f607ed65dd32876ff25881f2b6b"
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: X-Request-Hash: request:search:27c14c264a514e16a400698fd3622304b3a7f99a
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: X-Request-Cached: 1
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: X-Request-Cache-Ttl: 404176
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Expires: Sun, 26 Jan 2020 11:19:35 GMT
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: Vary: Accept-Encoding
2020-01-21 20:46:32.152 5817-5946/r D/OkHttp: X-Cache-Status: HIT
2020-01-21 20:46:32.154 5817-5946/r D/OkHttp: {"request_hash":"request:search:27c14c264a514e16a400698fd3622304b3a7f99a","request_cached":true,"request_cache_expiry":404176,"results":

//:GETS DATA

2020-01-21 20:46:32.168 5817-5946/r D/OkHttp: <-- END HTTP (31375-byte body)
2020-01-21 20:46:32.214 5817-5817/r D/AndroidRuntime: Shutting down VM
2020-01-21 20:46:32.232 5817-5817/r E/AndroidRuntime: FATAL EXCEPTION: main
    Process: r, PID: 5817
    java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List r.SearchResponse.getResult()' on a null object reference
        at r.SearchResultAdapter.getItemCount(SearchResultAdapter.java:60)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3834)
        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3330)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24530)
        at androidx.constraintlayout.widget.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:1227)
        at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1572)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at com.android.internal.policy.DecorView.onMeasure(DecorView.java:742)
        at android.view.View.measure(View.java:24530)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3006)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1833)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2122)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
        at android.view.Choreographer.doCallbacks(Choreographer.java:790)
        at android.view.Choreographer.doFrame(Choreographer.java:725)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Here you can see that the app actually gets some data but it crashes after that.

I made a class for Search (SearchResponse):

package r;
import android.os.Parcel;
import android.os.Parcelable;

import com.google.gson.annotations.SerializedName;

import org.apache.commons.lang3.builder.ToStringBuilder;

import java.util.List;

public class SearchResponse implements Parcelable {

    public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() {
        @Override
        public SearchResponse createFromParcel(Parcel in) {
            return new SearchResponse(in);
        }

        @Override
        public SearchResponse[] newArray(int size) {
            return new SearchResponse[size];
        }
    };
    @SerializedName("request_hash")
    private String requestHash;
    @SerializedName("request_cached")
    private Boolean requestCached;
    @SerializedName("result")
    private List<Result> result;
    @SerializedName("result_last_page")
    private Integer resultLastPage;

    protected SearchResponse(Parcel in) {
        requestHash = in.readString();
        byte tmpRequestCached = in.readByte();
        requestCached = tmpRequestCached == 0 ? null : tmpRequestCached == 1;
        result = in.createTypedArrayList(Result.CREATOR);
        if (in.readByte() == 0) {
            resultLastPage = null;
        } else {
            resultLastPage = in.readInt();
        }
    }

    public static Creator<SearchResponse> getCREATOR() {
        return CREATOR;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("requestHash", requestHash).append("requestCached", requestCached).append("result", result).append("resultLastPage", resultLastPage).toString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(requestHash);
        dest.writeByte((byte) (requestCached == null ? 0 : requestCached ? 1 : 2));
        dest.writeTypedList(result);
        if (resultLastPage == null) {
            dest.writeByte((byte) 0);
        } else {
            dest.writeByte((byte) 1);
            dest.writeInt(resultLastPage);
        }
    }

    public String getRequestHash() {
        return requestHash;
    }

    public void setRequestHash(String requestHash) {
        this.requestHash = requestHash;
    }

    public Boolean getRequestCached() {
        return requestCached;
    }

    public void setRequestCached(Boolean requestCached) {
        this.requestCached = requestCached;
    }

    public List<Result> getResult() {
        return result;
    }

    public void setResult(List<Result> result) {
        this.result = result;
    }

    public Integer getResultLastPage() {
        return resultLastPage;
    }

    public void setResultLastPage(Integer resultLastPage) {
        this.resultLastPage = resultLastPage;
    }
}

And I made a class for the base URL:

public class AnimesAPI {


    private static Retrofit retrofit = null;

    public static Retrofit getRetrofit() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).addInterceptor(interceptor).build();

        retrofit = new Retrofit.Builder()
                .baseUrl("https://api.jikan.moe")
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();

        return retrofit;

    }

and for the search I added this:

public interface AnimesAPIInterface
{

    @GET("https://api.jikan.moe/v3/search/anime")
    Call<SearchResponse> searchByAnimeTitle(@Query("q") String title);

}

Solution

  • Take a good look here:

    @SerializedName("result")
    private List<Result> result;
    

    You're missing an s at the end, in the serialized name.

    Should be:

    @SerializedName("results")
    private List<Result> result;
    

    I actually pasted the link into my browser, and looking at the relevant JSON, here's an excerpt:

    ..."results":[{"mal_id":20,"url":"https:\/\/myanimelist.net\/anime\/...
    

    Pay attention to the serialized names, they must match perfectly. You can use JSON formatter to better view the JSON, I linked the one I use, there's probably plenty others too.