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 is a HTTP client to some web servers accessible identified by their URLs. 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.0.9'
- 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, which opens the 2nd Activity, passing city name as an intent extra.
2. Getting Current Weather for a City using a JSON weather API
2.1 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 WeatherStack, 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.
WeatherStack API documentation can be found here. Note how the access_key and query parameters are expected to be passed to the request URL.
- Because secure HTTPS requests are not supported by free WeatherStack plan, we have to enable plainText HTTP traffic for our app by adding
android:usesCleartextTraffic="true"
to AndroidManifest (within the <application> tag)
- In onCreate of CityDetailsActivity, perform a request to fetch that city's current weather (API endpoint URL: http://api.weatherstack.com/current).
- You can directly use the city name along with country in the same format you got it from part 1.
- 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 WeatherStack documentation), we can assemble code to extract temperature value as a float, like so:
- 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
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 the same as we did for Firebase cloud storage.
- The easy way of setting it up is to use Firebase Assistant within Android Studio (Tools -> Firebase -> Cloud Messaging )
- Before proceeding, make sure you have the correct google-services.json file to your project and the additional entries in build.gradle file, such as
classpath 'com.google.gms:google-services:4.3.4'
- 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:20.3.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
- Start by acquiring the registration token, you only need to log the token, so you may simplify the code snippet provided in the exmaple.
- You can add the code to Activity onCreate().
- When you 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 "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, override its' onMessageReceived method
- 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>
- 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
4. Bonus: Wikipedia 'nearby' API
- The Weather API response contains among other things, coordinates for each queried location.
- MediaWiki has a Geosearch module, which allows to query for Wikipedia entries based on coordinates.
- The base URL for this API is
https://en.wikipedia.org/w/api.php
- This following example makes a request for nearby wikipedia pages and their images with coordinates of Riga:
https://en.wikipedia.org/w/api.php?action=query&generator=geosearch&prop=pageimages|description&pithumbsize=400&inprop=url&ggsradius=1000&ggslimit=5&format=json&ggscoord=56.950|24.100
Here's a breakdown of the request:
URL Param | Description |
---|---|
generator=geosearch | use the geosearch module |
prop=pageimages|description | Include page images and description texts in response |
pithumbsize=400 | set page image thumbnail size to 400 pixels |
inprop=url | include URLs in the attached properties (such as pageimages) |
ggsradius=1000 | geosearch within 1000 meters |
ggslimit=5 | return max 5 results |
format=json | produce json output |
ggscoord=56.950|24.100 | the geosearch coordinates |
Based on the above example, try to update the app, so that based on the coordinates from the weather API, you pull also data about nearby Wiki pages. When you get the response from the wiki API, you can further make more requests to download the image URLs contained in the Wikipedia response.
- displayAttraction( .. ) method in CityActivity.kt will help you show results in UI once you have parsed them.
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)
Bonus: 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)