简体   繁体   中英

Unable to fetch data from webservice with Retrofit

I'm trying to load some very simple data in JSON format, generated by a webservice, using Retrofit and Gson and also trying to implement MVVM architecture, but I'm getting a null value when calling data from ViewModel : I've seen many SO questions (or maybe not so many) but can't figure out where and why I'm getting this problems.

So, what I have is this

JSON RESPONSE : This could be either this one:

{
    "ok": [
        {
            "id": 1,
            "username": "test1",
            "pass": "123",
            "reg_date": "2020-05-20 00:00:00"
        }
    ]
}

or this:

{
    "error": [
        {
            "message":"user doesn't exists"
        }
    ]
}

I've created the correspondign POJOs for each response type, which I'll omit for simplicity. But also I've created a generic POJO for handling the response (thanks to @Công Hải, )

**POJOGenericResponse.java*

public class POJOGenericResponse{
    @SerializedName("ok")
    private LiveData<ArrayList<POJOUsr>> user;
    @SerializedName("error")
    private LiveData<ArrayList<POJOError>> error;

    //Constructor and Getters...
}

JsonApi.java

public interface JsonApi {
    @GET("SelectAllUsr.php")
    Call<LiveData<POJOGenericResponse>> getGenericResponse();
}

I don't know how this class should be called, according to its purpose and name conventions

RetrofitInstance.java

public class RetrofitInstance {
    private static String URL_BASE = "http://192.168.0.35/app_android/webservices/";
    private static Retrofit retrofit;
    private static Gson gson;

    public Retrofit getRetrofitInstance(){
        gson = new GsonBuilder().setLenient().create();
        if(retrofit == null){
            retrofit = new Retrofit.Builder()
                    .baseUrl(URL_BASE)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }
        return retrofit;
    }
}

LoginRepository.java

public class LoginRepository{
    private RetrofitInstance retrofitInstance;
    private JsonApi jsonApi;
    private LiveData<POJOGenericResponse> genericResponse;
    private Call<LiveData<POJOGenericResponse>> call;

    public LoginRepository() {
        retrofitInstance = new RetrofitInstance();
        jsonApi = retrofitInstance.getRetrofitInstance().create(JsonApi.class);
        call = jsonApi.getRespuestaGenerica();
    }

    //TODO: This should be done in another class??
    public LiveData<POJOGenericResponse> getGenericResponse() {
        call.clone().enqueue(new Callback<LiveData<POJOGenericResponse>>() {

            @Override
            public void onResponse(Call<LiveData<POJOGenericResponse>> call, Response<LiveData<POJOGenericResponse>> response) {
                if (response.isSuccessful()) {
                    genericResponse= response.body();
                } else {
                    //
                }
            }

            @Override
            public void onFailure(Call<LiveData<POJOGenericResponse>> call, Throwable t) {
                //
            }
        });
        return genericResponse;
    }
}

LoginViewModel.java

public class LoginViewModel extends AndroidViewModel {
    private RepositorioLogin loginRepository;
    private MutableLiveData<POJOGenericResponse> serverResponse;

    public ViewModelLogin(@NonNull Application application) {
        super(application);
        loginRepository = new RepositorioLogin();
        serverResponse = new MutableLiveData<>();
    }

    public void setServerResponse(){
        serverResponse.setValue(loginRepository.getGenericResponse().getValue());
    }

    public LiveData<POJORespuestaGenerica> getServerResponse(){
        return serverResponse;
    }
}

Here's where the data should be shown

FragmentData.java

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_data, container, false);
        TextView exampleText = v.findViewById(R.id.tv_test);

        LoginViewModel loginViewModel = new ViewModelProvider(requireActivity()).get(LoginViewModel.class);
        loginViewModel.getServerResponse().observe(requireActivity(), new Observer<POJORespuestaGenerica>() {
            @Override
            public void onChanged(POJOGenericResponse pojoGenericResponse) {
                if (pojoGenericResponse.getUser() != null) {
                    exampleText.setText(pojoGenericResponse.getUser().getValue().get(0).getUsername());
                }
                if (pojoGenericResponse.getError() != null) {
                    exampleText.setText(pojoGenericResponse.getError().getValue().get(0).getMensaje());
                }
            }
        });

When debugging, the error that I get is

response = Cannot find local variable 'response' serverResponse.setValue(loginRepository.getGenericResponse().getValue());\n = java.lang.NullPointerException response = Cannot find local variable 'response'

which describe the problem by itself, but can't manage to solve it.

Also, any recommendation on the implementation of the MVVM architecture will be very appreciated.

Beforehand, thank you very much

Looks like this is an incorrect bit --

public LiveData<POJOGenericResponse> getGenericResponse() {
        call.clone().enqueue(new Callback<LiveData<POJOGenericResponse>>() {

            @Override
            public void onResponse(Call<LiveData<POJOGenericResponse>> call, Response<LiveData<POJOGenericResponse>> response) {
                if (response.isSuccessful()) {
                    genericResponse= response.body();
                } else {
                    //
                }
            }

            @Override
            public void onFailure(Call<LiveData<POJOGenericResponse>> call, Throwable t) {
                //
            }
        });
        return genericResponse;
    }

So onResponse is an asynchronous call and you are returning the return genericResponse; immediately from the method.

Rather than getting a return from this method change it to void and directly observe on private LiveData<POJOGenericResponse> genericResponse;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM