ovicko
ovicko

Reputation: 2302

Android MVVM and Retrofit api response is null

This function is used to Login the user. I am using data binding and RxJava

public void loginUser(String idNumber, String password) {
    RetrofitService.getApiService()
            .agentLogin(new LoginRequest(idNumber, password))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<Response<LoginResponse>>() {
                @Override
                public void call(Response<LoginResponse> userResponse) {
                    if (userResponse.isSuccessful() ) {
                        executor.execute(new Runnable() {
                            @Override
                            public void run() {

                                Log.i("AUTHKEY",userResponse.body().getUser().getAuthKey());
                                userDao.insert(userResponse.body().getUser());
                            }
                        });
                    }
                }
            });

}

The app fails when I log Log.i("AUTHKEY",userResponse.body().getUser().getAuthKey());

Error java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Integer com.vuna.agent.model.LoginResponse$User.getId()' on a null object reference

From the Okhttp logger, the server returns the right response

2019-04-12 12:33:36.707 28896-29064/com.vuna.agent D/OkHttp: <-- 200 OK http://example.com/login (923ms)

2019-04-12 12:33:36.709 28896-29064/com.vuna.agent D/OkHttp: {"code":201,"message":"You are now logged in!","id":2,"firstname":"Victor","lastname":"Amwollo","authkey":"22sA1JvVo4hmzhps1NVIO7_B6f4yCp_e"}
2019-04-12 12:33:36.709 28896-29064/com.vuna.agent D/OkHttp: <-- END HTTP (141-byte body)

Here is the LoginResponse class with an inner Entity class

public class LoginResponse {

    private Boolean success;

    private User user;


    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }


    @Entity(tableName = "user")
    public static class User {

        @PrimaryKey(autoGenerate = true)
        @SerializedName("id")
        private Integer id;

        private String firstName;
        private String lastName;

        private String authKey;

        //getters and setters

    }
}

Why am I getting the error?

Upvotes: 1

Views: 1215

Answers (2)

Stuart Campbell
Stuart Campbell

Reputation: 1147

You get the null pointer because your json does not map to LoginResponse.

try

public class LoginResponse {

    private Boolean success;
    private Integer id;
    private String firstName;
    private String lastName;
    private String authKey;

}

I cleaned up your RX. Note: Brackets might be wrong as I am used to kotlin.

public void loginUser(String idNumber, String password) {
    RetrofitService.getApiService()
        .agentLogin(new LoginRequest(idNumber, password))
        .filter { it.isSuccessful(); }
        .map { it.body(); }
        .map { new User(it.getId()...) /* covert LoginResponse to User here */ } 
        .flatMapCompletable { Completable.fromAction { userDao.insert(it); } }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe();                                 
}

Upvotes: 1

Jeel Vankhede
Jeel Vankhede

Reputation: 12138

The response.body() method on the response will read the input (network) stream and convert it into a string. So it dynamically builds the string and returns it to you. The second time you call it, the network stream has already been consumed and is no longer available.

So, solution is to save the result of response into a variable, and then access it as many times as needed.

Check out below code:

@Override
public void call(Response<LoginResponse> userResponse) {
    if (userResponse.isSuccessful() ) {
        LoginResponse data = userResponse.body(); // Consume response body once and then use it in this method further
        executor.execute(new Runnable() {
            @Override
            public void run() {
                Log.i("AUTHKEY", data.getUser().getAuthKey()); // access data variable here
                userDao.insert(data.getUser()); // access data variable here
            }
        });
    }
}

Upvotes: 2

Related Questions