- What I'm trying to do : I am trying to show a list of cities in a
RecyclerView
in theMainActivity
and whenever a city in the list is clicked aWeatherDetailsActivity
should fetch a response from theOpenWeather's API
usingRetrofit
. I'm usingMVVM
architecture. - What I've managed to do so far : I've got the list showing up perfectly and even the response is fetched properly according to a
POJO
model, but when I click on a city it crashes due toNullPointerException
. - TL;DR : I don't understand why the
return weatherResult;
in theWeatherRepository.java
always return null.
- Code:
WeatherRepository.java
public class WeatherRepository {
final String TAG = this.getClass()
.getSimpleName();
private static final String WEATHER_API_URL = "https://api.openweathermap.org/";
private static WeatherRepository weatherRepository;
private APIService apiService;
MutableLiveData weatherResult;
private WeatherRepository() {
Retrofit retrofit = new Retrofit.Builder().baseUrl(WEATHER_API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiService = retrofit.create(APIService.class);
}
public synchronized static WeatherRepository getInstance() {
if (weatherRepository == null) {
return new WeatherRepository();
}
return weatherRepository;
}
public MutableLiveData<WeatherResult> getWeatherData(String city, String appid) {
apiService.getWeatherData(city, appid)
.enqueue(
new Callback<WeatherResult>() {
@Override
public void onResponse(Call<WeatherResult> call, Response<WeatherResult> response) {
if (response.isSuccessful()) {
Log.i(TAG, "onResponse: " + response.body()
.toString());
//response.body() gets the value perfectly.
weatherResult.setValue(response.body());
}
}
@Override
public void onFailure(Call<WeatherResult> call, Throwable t) {
t.printStackTrace();
Log.d(TAG, "onFailure: " + t.getMessage());
}
});
return weatherResult; //this always return null
}
}
WeatherViewModel.java
public class WeatherViewModel extends ViewModel
{
private String TAG = this.getClass().getSimpleName();
private MutableLiveData<WeatherResult> data = new MutableLiveData<>();
private String APP_ID = /*secret api key*/;
public void getWeather(String city)
{
data.setValue(WeatherRepository.getInstance().getWeatherData(city,APP_ID).getValue()); // this line throws NullPointerException.
Log.d(TAG, "WeatherViewModel: "+(data.toString()));
}
public LiveData<WeatherResult> getCityDetails()
{
return data;
}
}
APIService.java
public interface APIService {
//Weather API
//http://samples.openweathermap.org/data/2.5/weather?q=London&appid={APP_ID}
@GET("data/2.5/weather?")
Call<WeatherResult> getWeatherData(@Query("q") String city,@Query("appid") String appID);
}
WeatherDetails.java
public class WeatherDetails extends AppCompatActivity {
WeatherViewModel viewModel;
TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.weather_details);
textView = findViewById(R.id.weather_info_text);
viewModel = ViewModelProviders.of(this).get(WeatherViewModel.class);
}
@Override
protected void onStart() {
super.onStart();
viewModel.getCityDetails().observe(this, new Observer<WeatherResult>() {
@Override
public void onChanged(WeatherResult weatherResult) {
textView.setText(weatherResult.toString()); //this line throws NullPointerException
}
});
}
}
All I want is to return the Retrofit response
and set it into the MutableLiveData<WeatherResult>
so that it is observed inside the WeatherDetails.java
and the UI is updated accordingly.
I don't get why the return weatherResult;
in the WeatherRepository.java
always returns null
Now I'm not sure how to proceed, I'm at the end of the ropes so I'm asking it here.
Incase you want to look at the entire code here is the code on github. Thank you for every and any help.
UPDATE
I did the following changes to resolve NullPointerException
and the crash is fixed. link to diff
UPDATE 2
Here is the final working code, what did it for me was to get rid of a MutableLiveData
in my ViewModel
class and instead use a public method
in my ViewModel
as a wrapper to call the repository service call to the API server
. Here is the link to the diff. Thank you to everyone who helped.