How to develop an Android Weather app using Weatherlib
Some users asked to me some details on how to use the Weatherlib. In this post want to describe how to use it.
This is an Android lib that helps to develop fast and easily weather app. This post describes the main concepts that stand behind the lib so that you can understand it better.
If you are not interested about the internal library structure you can jump to the part that describes how to code a weather client :
Weatherlib structure
The library is built by three different layers:
- Data model
- Weather providers
- Weather client
The first layer, the data model, represents the information extracted by different weather provider. It abstracts this information so that, independently the weather provider you choose, you have always the same information presented in the same manner and with the same relations between them.
Weather providers are connectors toward the weather provider. By now the library supports three weather provider:
This layer implements the logic necessary to extract information from the weather provider, parsing the data retrieved and at the end of the process these providers populate the data model.
The last layer is the client layer. This layer implements the logic to connect the weather provider (i.e. using HTTP connection) and handle the connection errors and so on. Once the data is retrieved the client uses the weather provider to parse the data.
Each layer can be extended and customized easily so that you can support other weather providers or implements a different way to connect to the providers. We will see later how to do it.
Weatherlib data model
This is the model that stands at the library base and you interact with it when you want to get weather information.
The CurrentWeather class holds all the information related to the current weather data. The WeatherHourForecast holds the information related to the hourly weather data and the WeatherForecast holds the information related to the next days weather.
Weatherlib provider
This layer is the heart of the library because it implements the logic to extract the information from the weather provider. By now the library supports three different weather provider. Each provider is described by three different classes that must implements three interfaces:
IProviderType
that define the class name that must be instantiated to create the provider.IWeatherProvider
the main interface that all the weather provider must implement and holds all the method necessary to parse the data.IWeatherCodeProvider
a simple interface to translate the weather code in a common format so that the weather code condition are indiependent from the provider.
For example if we look at the openweathermap provider we have:
All the provider are created by the WeatherProviderFactory
.
Weather client
This is the last layer that handles the connection with the remote weather provider and retrieves the data. You can use all the protocols you like but generally HTTP protocol is used. These classes handle also the connection errors and so on. The lib provides two different implementation:
- One based on volley lib
- One based on standard HttpConnection provided by Android
Setup the dev enviroment
To use the lib is very simple, because the lib is in the central maven repository so you don’t have to do much work in Android studio. Once you created your project you have to add a new dependency to the project build.gradle
dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.survivingwithandroid:WeatherLib:1.3.0' }
How to develop a Weather app using Weatherlib
Now you know the main library layers it is the time to start using the library. Of course when you develop you don’t have to worry about the internal details explained by now but you will use just a few things. The first thing you have to do is getting an instance of the WeatherClient
so that you can use in your app. This class is singleton so you have to initialize it only one time:
WeatherClient client = WeatherClientDefault.getInstance();
in this case we want to use the default client based on Volley lib otherwise you could use the StandardHttpClient. Now we have our client we have to initialize it passing the Context:
client.init(ctx);
now we have to configure our client passing to it the weather configuration:
WeatherConfig config = new WeatherConfig(); config.unitSystem = WeatherConfig.UNIT_SYSTEM.M; config.lang = "en"; // If you want to use english config.maxResult = 5; // Max number of cities retrieved config.numDays = 6; // Max num of days in the forecast
and then:
client.updateWeatherConfig(config);
Now the client is configured, the last step is choose what kind of provider we want to use:
IWeatherProvider provider = null; try { //provider = WeatherProviderFactory.createProvider(new YahooProviderType(), config); provider = WeatherProviderFactory.createProvider(new OpenweathermapProviderType(), config); //provider = WeatherProviderFactory.createProvider(new WeatherUndergroundProviderType(), config); client.setProvider(provider); } catch (Throwable t) { // There's a problem }
Ok done! Our client is ready to use and ready to retrieve the information.
Search for city id
Usually the next step is searching for the city. Almost all the weather provider wants a city id and you have to find it before asking weather information. Now you can have two options:
- Searching it by name or part of the name
- Searching it by geographic coordinates
In the first case you can use:
private void search(String pattern) { client.searchCity(pattern, new WeatherClient.CityEventListener() { @Override public void onCityListRetrieved(List<City> cityList) { // When the data is ready you can implement your logic here } @Override public void onWeatherError(WeatherLibException t) { // Error } @Override public void onConnectionError(Throwable t) { // Connection error } }); }
where pattern is the partial city name you are looking for. In the onCityListRetrived
you have a city list as reasult that can be passed to an ArrayAdapter for example to show the result to the user.
In the second option, using geographic coordinates, you simply have:
client.searchCityByLocation(WeatherClient.createDefaultCriteria(), new WeatherClient.CityEventListener() { @Override public void onCityListRetrieved(List<City> cityList) { // Here your logic when the data is available } @Override public void onWeatherError(WeatherLibException wle) { } @Override public void onConnectionError(Throwable t) { } }); } catch(LocationProviderNotFoundException lpnfe) { }
Notice that at line 1 we used WeatherClient.createDefaultCriteria(), this method returns some default criteria that are used to select the best location provider. If you want to use your criteria you simply set them manually:
Criteria criteria = new Criteria(); criteria.setPowerRequirement(Criteria.POWER_LOW); criteria.setAccuracy(Criteria.ACCURACY_COARSE); criteria.setCostAllowed(false);
Current Weather condition
Once you have the city id you can query the provider asking for the current weather condition:
client.getCurrentCondition(cityId, new WeatherClient.WeatherEventListener() { @Override public void onWeatherRetrieved(CurrentWeather weather) { // Here we can use the weather information to upadte the view } @Override public void onWeatherError(WeatherLibException t) { } @Override public void onConnectionError(Throwable t) { } });
Notice that the client use a callback interface the notify the caller that the data is ready. This happens only if you use the WeatherClientDefault
. Using these callback methods you don’t have to worry about ANR problems because the HTTP request happens in a separate thread and those request don’t block the app. When the data is ready at line 3 we can use it to update the UI, as the snippet below:
@Override protected void updateView(Object obj) { CurrentWeather weather = (CurrentWeather) obj; cityText.setText(weather.location.getCity() + "," + weather.location.getCountry()); condDescr.setText(weather.currentCondition.getCondition() + "(" + weather.currentCondition.getDescr() + ")"); temp.setText("" + ((int) weather.temperature.getTemp())); unitTemp.setText(weather.getUnit().tempUnit); .... }
WeatherForecast and Hourly WeatherForecast
If you want you can use the lib to query the forecast and the hourly forecast data. In the same way described above we have:
client.getForecastWeather(cityId, new WeatherClient.ForecastWeatherEventListener() { @Override public void onWeatherRetrieved(WeatherForecast forecast) { updateView(forecast); } @Override public void onWeatherError(WeatherLibException t) { } @Override public void onConnectionError(Throwable t) { //WeatherDialog.createErrorDialog("Error parsing data. Please try again", MainActivity.this); } });
and for the hourly forecast we have:
client.getHourForecastWeather(cityId, new WeatherClient.HourForecastWeatherEventListener() { @Override public void onWeatherRetrieved(WeatherHourForecast forecast) { updateView(foreacst); } @Override public void onWeatherError(WeatherLibException wle) { } @Override public void onConnectionError(Throwable t) { } });
Cache Data
One aspect that should be considered while developing a weather app is the weather data don’t change fast so it could be a good practice to cache the data so that you don’t have to request them all the time to the remote provider. The Weatherlib doesn’t provide a caching mechanism by now so it has to implemented by you if you want to cache the results retrieved. This can be done easily, creating a singleton class that holds the data you retrieve, for example:
public class AppWeatherClient { private static AppWeatherClient me; private Context ctx; private WeatherClient client; private WeatherConfig config; private WeatherForecast forecast; private CurrentWeather weather; private AppWeatherClient() {} public static AppWeatherClient getInstance() { if (me == null) me = new AppWeatherClient(); return me; } public CurrentWeather getCurrentWeather() { return this.weather; } public void setCurrentWeather(CurrentWeather weather) { this.weather = weather; } public WeatherForecast getForecast() { return forecast; } public void setForecast(WeatherForecast forecast) { this.forecast = forecast; } }
Using API key
Some providers requires you to use a API key (i.e Weatherundergroud). This API key is necessary to invoke the remote methods and retrieve the weather information. Weatherlib provides an easy way to handle this kind of requests and you don’t have to worry how to send the key to the remote provider. In the WeatherConfig class, you can add your key:
config.ApiKey=your_key
That’s all.
Contribute
The library source code is free so that everyone that wants to contribute to the library is welcome.If you want to contribute to the library you can use github and pull a new request so that I can merge your code. If you find bugs in the code you can open an issue on github or contact me.
In case you use the library and you are satisfied with it I would like you could send some screenshots of the app you created. You can join the g+ community and post here the screenshots, you can join the community if you want simply to tell your opinions or suggest some improvements.
If someone wants to contribute freely to the library providing a nice logo I would be very happy.
I know some users experienced some problems importing the lib in the Eclipse if someone already did it and wants to explain it you are free to contribute.
Reference: | How to develop an Android Weather app using Weatherlib from our JCG partner Francesco Azzola at the Surviving w/ Android blog. |