My app has weather implemented into it and it is using the data from OpenWeatherMap.
The user can input the city that they would like the weather for via an Alert Dialog code but the app crashes when they input an invalid city.
Which in turn changes the URL to:
http://api.openweathermap.org/data/2.5/weather?q={Invalid City Here} &units=metric&APPID={APP ID HERE}
and the API returns {"cod":"404","message":"city not found"}, which causes my app to crash!
My question is: How to handle that 404 code so that the app doesn't crash but notifies the user that they need to type in a valid City. - I apologise if this is a waste of time for you guys, but I am still learning and am quite new to all this... More detailed explanations and non-condescending guidelines would be very appreciated and would help me learn!
The alert dialog is below:
private void showChangeCityDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.change_city_title);
final EditText cityInput = new EditText(MainActivity.this);
cityInput.setInputType(InputType.TYPE_CLASS_TEXT);
cityInput.setHint("Sofia,BG");
builder.setView(cityInput);
builder.setPositiveButton(R.string.submit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (cityInput.getText().toString().equals("")) {
Toast.makeText(MainActivity.this, "Please type a City name!", Toast.LENGTH_LONG).show();
return;
}
CityPreference cityPreference = new CityPreference(MainActivity.this);
cityPreference.setCity(cityInput.getText().toString());
String newCity = cityPreference.getCity();
renderWeatherData(newCity);
}
});
builder.show();
}
This is the code for the renderWeatherData:
public void renderWeatherData(String city) {
WeatherTask weatherTask = new WeatherTask();
weatherTask.execute(new String[]{city + "&units=metric"});
}
The error upon crashing is:
07-25 19:03:28.507 24691-25279/? E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #2
Process: com.vladimirtumbev.android.footballscorekeeper, PID: 24691
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:309)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116)
at org.json.JSONTokener.nextValue(JSONTokener.java:94)
at org.json.JSONObject.<init>(JSONObject.java:156)
at org.json.JSONObject.<init>(JSONObject.java:173)
at data.JSONWeatherParser.getWeather(JSONWeatherParser.java:24)
at com.vladimirtumbev.android.footballscorekeeper.MainActivity$WeatherTask.doInBackground(MainActivity.java:465)
at com.vladimirtumbev.android.footballscorekeeper.MainActivity$WeatherTask.doInBackground(MainActivity.java:458)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Both my classes that extend the AsyncTask:
private class DownloadImageAsyncTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
iconView.setImageBitmap(bitmap);
}
private Bitmap downloadImage(String code) {
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet getRequest = new HttpGet(Utils.WEATHER_ICON_URL + code + ".png");
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.e("DownloadImage", "Error:" + statusCode);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
inputStream = entity.getContent();
//decode contents from the stream
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
private class WeatherTask extends AsyncTask<String, Void, Weather> {
@Override
protected Weather doInBackground(String... params) {
String data = ((new WeatherHTTPClient()).getWeatherData(params[0]));
weather = JSONWeatherParser.getWeather(data);
weather.iconData = weather.currentCondition.getIcon();
new DownloadImageAsyncTask().execute(weather.iconData);
return weather;
}
@Override
protected void onPostExecute(Weather weather) {
super.onPostExecute(weather);
DecimalFormat decimalFormat = new DecimalFormat("#.#");
String tempFormat = decimalFormat.format(weather.currentCondition.getTemperature());
cityName.setText(weather.place.getCity() + "," + weather.place.getCountry());
temp.setText("" + tempFormat + "°C");
description.setText(weather.currentCondition.getCondition() + "(" + weather.currentCondition.getDescription() + ")");
}
}
My HTTP helper class:
public class WeatherHTTPClient {
public String getWeatherData(String place){
HttpURLConnection connection = null;
InputStream inputStream = null;
try {
connection = (HttpURLConnection) (new URL(Utils.WEATHER_BASE_URL + place + Utils.WEATHER_API_KEY)).openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
// connection.setDoOutput(true);
connection.connect();
if(connection.getResponseCode() == 200) // 200 is ok
{
//Read the Response
StringBuffer stringBuffer = new StringBuffer();
inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line + "\r\n");
}
inputStream.close();
connection.disconnect();
return stringBuffer.toString();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
My JSON Weather Parser class:
public class JSONWeatherParser {
public static Weather getWeather(String data){
Weather weather = new Weather();
//Create JSON object from data
try {
JSONObject jsonObject = new JSONObject(data);
Place place = new Place();
JSONObject coordObj = Utils.getObject("coord", jsonObject);
place.setLat(Utils.getFloat("lat", coordObj));
place.setLon(Utils.getFloat("lon", coordObj));
//get Sys object
JSONObject sysObj = Utils.getObject("sys", jsonObject);
place.setCountry(Utils.getString("country",sysObj));
place.setCity(Utils.getString("name",jsonObject));
weather.place = place;
//get Weather info
JSONArray jsonArray = jsonObject.getJSONArray("weather");
JSONObject jsonWeather = jsonArray.getJSONObject(0);
weather.currentCondition.setWeatherId(Utils.getInt("id", jsonWeather));
weather.currentCondition.setDescription(Utils.getString("description", jsonWeather));
weather.currentCondition.setCondition(Utils.getString("main", jsonWeather));
weather.currentCondition.setIcon(Utils.getString("icon", jsonWeather));
JSONObject mainObj = Utils.getObject("main", jsonObject);
weather.currentCondition.setTemperature(Utils.getDouble("temp", mainObj));
JSONObject cloudObj = Utils.getObject("clouds", jsonObject);
weather.clouds.setPrecipitation(Utils.getInt("all", cloudObj));
return weather;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
I have some more Model classes with setters and getters and can add them if needed.
Thank you for the help!!!