Unknown19 Unknown19 - 3 months ago 20
Java Question

Retrofit onResponse method not working, throws NullPointerException

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?

Answer

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);
Comments