Reputation: 2302
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
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
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