Lab 8 - Invoking web services and parsing JSON, Cloud Messaging
Table of contents
Introduction - HTTP requests with Ion
In this lab we'll create an app which uses a HTTP client to access some web services. The data is returned by the server in a specific format, which the client has to parse before it can do something useful with it.
We will use the Ion library to make HTTP requests. Ion is a 3rd-party library which:
- performs the requests in the background for you
- provides some helper methods which map the HTTP response into different types: String, JsonObject, Bitmap, etc.
- invokes code specified by a callback function once the response is received
Take a look at the Ion Github page to see examples of how requests are made.
Alternatives to Ion include Volley and OkHTTP. Android also has built-in capabilities for HTTP requests but they are generally more verbose to use.
0. Base project
Download and open the base Android Studio Project. The project has some simple UI definitions (2 activities) to speed up the lab for you.
1. Basic HTTP Request - List of Cities
The first task is to get a text file which lists cities along the coast of the Baltic Sea. The file is hosted on the UT network, at https://kodu.ut.ee/~jaks/baltic_sea/cities.txt
- Refer to the Ion documentation & examples and perform the request to load the above URL with Ion, using
asString()
as the return format.- To add Ion to your project:
implementation 'com.koushikdutta.ion:ion:3.1.0'
- To add Ion to your project:
- Split the response String into a list using
\n
character as the separator. You don't have to separate the city from the country name. - Using the existing addToListView() method, add a Button for each individual city to your Apps UI
- addToListView() sets up a click listener to a function
openCity(..)
. Finish the implementation ofopenCity
, and notice the comments under that
- addToListView() sets up a click listener to a function
2. Getting Current Weather for a City using a JSON weather API
2.1A OpenWeatherMap API
- We will use a Weather API service called OpenWeatherMap. Please sign up for the free version of the service https://home.openweathermap.org/users/sign_up .
- When your account is created, navigate to the API Keys section of the webpage.
- If necessary, generate an API Key
- Note it down somewhere, we will need it to make requests to the API.
2.1B WeatherStack API
- We will use a Weather API service called WeatherStack. Please sign up for the free version of the service https://weatherstack.com/signup/free .
- When your account is created, you will see your personal API Access Key at the dashboard. Note it down somewhere, we will need it to make requests to WeatherStack.
2.2 HTTP GET request parameters
For simple HTTP GET requests, query parameters can be passed like so:
- http://www.example.com?param1=foo¶m2=bar , where a list of key-value pairs with format key=value are separated with a &, and the ? symbol separates the base URL from the parameters
In part 1, we did not use any request parameters. For the Weather Service, we will need to add our API key as a parameter, for example. To know the exact parameters and their names the web service expects, we should study their documentation.
- For OpenWeatherMap documentation, focus on the "Current" sub-part of the API, find the documentation here.
- WeatherStack API documentation can be found here. Focus on the "Current Weather" examples.
- Study the API: find an example response JSON, identify which parameters are required
- OpenWeatherMap notes:
- We want to pass the city name for the location, not coordinates (locate the Geocoding-related sub-part of the examples)
- Note how the appid and q parameters are expected to be passed to the request URL.
- WeatherStack notes:
- Note how the access_key and query parameters are expected to be passed to the request URL.
- Note, free WeatherStack plan doesn't support HTTPS, only HTTP
- In onCreate of CityDetailsActivity, perform a request to fetch that city's current weather.
- You can directly use the city name along with country in the same format you got it from part 1, no need to split it further.
- Specify Ion to handle the result as a Json with asJsonObject(), instead of asString()
2.3 Parsing Json response
- Now, we want to extract certain information from the result (temperature, wind information, weather descriptions (e.g. "Mist"), humidity etc). Ion uses the Gson library for working with the Json format.
- You can parse with Gson by using MyJsonObject.get(<<memberName>>) and then calling asJsonObject, asJsonArray, asString, asLong, etc, based on the structure of the Json that has been returned.
- Example: By knowing the response structure (study the example response of the API documentation), we can assemble code to extract temperature value as a float, like so (just generic exmaple):
- Now, we want to extract certain information from the result (temperature, wind information, weather descriptions (e.g. "Mist"), humidity etc). Ion uses the Gson library for working with the Json format.
<<myResponseJson>>.get("current").asJsonObject.get("temperature").asFloat
- Update the UI (TextViews, etc) to display the weather of the city
- The APIs also have an "icon" in the response. Try displaying the icon in an ImageView with Ion
- Note: For OpenWeatherMap, you need to assemble the full URL:
val iconurl = "https://openweathermap.org/img/w/" + iconcode + ".png"
- Note: For OpenWeatherMap, you need to assemble the full URL:
3. Firebase Cloud Messaging
Firebase Cloud Messaging is a service which allows to send messages & notifications to your app from the internet, even if its not running. In addition to such downstream messages (from cloud to the mobile), it also supports upstream messages (client to cloud), which then may be further forward to other mobiles, and so forth.
- Set up Firebase for your project. The process is very similar as we did for Firebase cloud storage.
- Option A: The easy way of setting it up is to use Firebase Assistant within Android Studio
(Tools -> Firebase -> Cloud Messaging )
- follow the steps there.- This adds the necessary dependencies and google-services.json file to your project automatically
- Option B: Do it in the browser in Firebase Console
- Option A: The easy way of setting it up is to use Firebase Assistant within Android Studio
- Before proceeding, make sure you have the correct google-services.json file in your project and the additional entries in the project-level build.gradle file, such as
classpath 'com.google.gms:google-services:4.3.10'
- Once you have the basic Firebase project set up and linked to you Android Studio project, add the Cloud Messaging dependency:
implementation 'com.google.firebase:firebase-messaging-ktx:22.0.0'
We can now follow the official firebase guide for getting started, but we will not be focusing on all steps.
3.1 Getting a registration token
This token uniquely identifies the Android device. In order to send push Notifications targeting specific clients (or groups of clients), we use this token as the identifier.
- Start by acquiring the registration token, you only need to log the token, so you may simplify the code snippet provided in the example.
- You can add the code to Activity onCreate().
- Launch your app, you should be able to see the Token value being logged, mark it down.
- Now close your application / put it in the background. We will try to send a notification to our app from FCM using the token.
- Go to Firebase Console / Cloud Messaging, select ("Create your first campaign" (On old dashboard: "Send your first message").
- Fill in a title (and description), then on the right you should see a button "Send Test Message).
- In the modal window, enter your Token, then hit send
- If you app was in the background, a notification should now appear. Clicking the notification takes you to your app.
Handling foreground messages
To handle messages while our app is running, we have to create a new Service that extends FirebaseMessagingService.
- Create the Kotlin class which does this.
- If you manually created the class (instead of Android Studio new Service wizard), add it to the manifest file.
- Add an intent filter to the service definition in manifest
<intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter>
- Override the services its' onMessageReceived method in the Kotlin file.
- Try to log the message contents, using the token-based test message as we did above.
- The Notification - related data can be obtained with
remoteMessage.notification.XXXX
- The extra key-value data attached to the notification can be obtained with
remoteMessage.data
- Try sending another message with extra attached data.
- Note: In Firebase console, you can do it with New Campaign -> Notification.
- Tip: If the messages don't seem to arrive, try using a fixed time value instead of "Now" for the Scheduling of the notification.
Troubleshooting
If you are getting an exception like
java.lang.ClassNotFoundException: Didn't find class com.google.android.gms.security.ProviderInstaller" on path: DexPathList
try adding the following to your Activity onCreate():
Ion.getDefault(applicationContext).getConscryptMiddleware().enable(false)
Optional: Weather Service Units
Based on the supported query parameters, ( https://weatherstack.com/documentation ), see if you can update the query so that the user can choose the units (imperial vs metric)