Retrofit onResponse method not working, throws Nul

2019-09-19 23:26发布

问题:

This question already has an answer here:

  • Retrofit 2 - null response body 1 answer
  • What is a NullPointerException, and how do I fix it? 12 answers

I'm trying to get data from web API, but something is wrong in my onResponse() method and I can't figure it out.

My JSON looks like this

  [
   "partner1",
   "partner2",
   "partner3",
     ...     ,
   "partner150"
  ]

I have Table partners (ActiveAndroid) in which I would like to save all partners from the API.

@Table(name = "Partners")
public class Partners extends Model {

    @Column(name = "Name")
    String name;

    public Partners() {}

    public Partners(String name) {
        this.name = name;
    }
}

Here is my POJO model class:

public class Partners extends ArrayList<String> {

@SerializedName("partners")
@Expose
public List<String> partners = new ArrayList<String>();

public List<String> getPartners() {
    return partners;
}

public void setName(List<String> partners) {
    this.partners = partners;
}

}

This is my interface

public interface APIService {

        @GET("Partners")
        Call<Partners> getPartners();
}

This is my APIHelper class

public class APIHelper {

    public static final String PARTNERS_URL = "https://part-of-link.com/partners.json/";

    public static APIService apiService;

    public static APIService getApiService() {
        if (apiService == null) {
            Retrofit retrofit = new Retrofit.Builder().baseUrl(PARTNERS_URL)
                    .addConverterFactory(GsonConverterFactory.create()).build();
            apiService = retrofit.create(APIService.class);
        }
        return apiService;
    }
}

And this is Fragment where I have a Button on which I would like to implement thw onClick() method to get the data from the API and save it into the Partners table.

public class DownloadMain extends Fragment implements Callback<Partners> {

    private Button dloadPartners;
    private Call<Partners> callPartners;
    public static APIService apiService;

    public DownloadMain() {}

    public DownloadMain newInstance() { return new DownloadMain(); }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.download_main, container, false);

        dloadPartners = (Button) view.findViewById(R.id.downloadPartners);
        dloadPartners.setOnClickListener(btnListener);

        callPartners = APIHelper.getApiService().getPartners();
        callPartners.enqueue(this);

        return view;
    }

    Button.OnClickListener btnListener = (new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            APIHelper helper = new APIHelper();
            apiService = helper.getApiService();

            Call<Partners> call = apiService.getPartners();
            call.enqueue(new Callback<Partners>() {
                @Override
                public void onResponse(Call<Partners> call, Response<Partners> response) {

        Call<Partners> call = apiService.getPartners();
        call.enqueue(new Callback<Partners>() {
            @Override
            public void onResponse(Call<Partners> call, Response<Partners> response) {

               List<String> partners =  response.body().getPartners();
                Log.d(TAG, "Number of partners received: " + partners.size());
                }

                @Override
                public void onFailure(Call<Partners> call, Throwable t) {
                    Toast.makeText(getActivity(), "FAIL!!!", Toast.LENGTH_SHORT).show();
                }
            });
        }
    });
    @Override
    public void onResponse(Call<Partners> call, Response<Partners> response) {


    }

    @Override
    public void onFailure(Call<Partners> call, Throwable t) {

    }
}

Here the onResponse() method throws me an error: Attempt to invoke virtual method 'java.util.List com.example...pojo.Partners.getPartners()' on a null object reference

What I want is to get all partners from web and save it into my Partners table on button click.

I can't find solution to this error so if someone could help me I've would be grateful.

Question: Could anybody guide me and tell me what is wrong and help me to fix this?

回答1:

Firstly, Java naming conventions. Partner represents a single entity, not plural of Partners. - Change your classname to reflect that. It's also confusing for us because your question has two classes of the same name. For your use case, making a class that extends an Arraylist is unnecessary.


Secondly, please learn proper JSON-to-Java conversions. Your JSON is simply an Array of Strings - the object extending the Arraylist is again unnecessary.

If you want to keep the ActiveAndroid model object and use it for Retrofit, the JSON needs to look like this for a List<Partner>

[
   {"name": "partner1"},
   {"name": "partner2"} 
] 

And for the current Partners class that extends an Arraylist with an inner Arraylist, something like this (which isn't valid JSON)

[
  "partners" : [
      "partner1", 
      "partner2"
  ] 
] 

So, again, you don't need the class that extends the Arraylist.


And finally, try to understand the "flow" of a Retrofit method usage.

You get a Call object of a certain type, you enqueue it to start the HTTP request, then you define a Callback with two actions for when the request succeeds and errors.

Now, see that you've implemented the Callback on the Fragment?

public class DownloadMain extends Fragment implements Callback<Partners> 

But you need to use that - you have those two empty methods for onResponse and onError at the bottom of the Fragment class... That's where you should implement what happens when Retrofit is done, and that's where the code goes when you have this line in onCreateView

callPartners.enqueue(this);

But since those methods are empty, nothing happens on that call. Meanwhile the other enqueue methods have unnecessary Callback implementations, when you could just use this

Additionally, no offense, but you seem to just be copy-pasting code around without knowing what's happening... Notice how you make a second, unnecessary API call.

// Call 1
Call<Partners> call = apiService.getPartners();
call.enqueue(new Callback<Partners>() {
    @Override
    public void onResponse(Call<Partners> call, Response<Partners> response) {
        // Call 2, despite having a response 
        Call<Partners> call = apiService.getPartners();
        call.enqueue(new Callback<Partners>() {
            @Override
            public void onResponse(Call<Partners> call, Response<Partners> response) {

With all these above problems, I'm not too surprised Retrofit would error out, but the problem can be avoided by first fixing the Retrofit Model, then be sure to check if response.body() is null before you attempt to use it.


If you do it all correctly, the only line of code in the onClick, needs to just be this.

callPartners.enqueue(DownloadMain.this);


回答2:

In the button click listener, the apiService is not initialised, although you are calling helper.getApiService();. So apiService instance is null, when calling Call call = apiService.getPartners();. hence NullPointerException.

Initialise apiService.

@Override
public void onClick(View v) {
    APIHelper helper = new APIHelper();
    apiService = helper.getApiService();

    Call<Partners> call = apiService.getPartners();


回答3:

Seems like your JSON response is just an array of strings, so I think you can do away with the POJOs and instead do this:

public interface APIService {

    @GET("Partners")
    Call<List<String>> getPartners();
}

while replacing every type parameter where you declared Partners with List<String> for example, replace all Call<Partners> with Call<List<String>>