This shows you the differences between two versions of the page.
smd:laboratoare:05 [2021/02/28 19:50] adriana.draghici [Lab 5. HTTP and web services] |
smd:laboratoare:05 [2021/05/18 22:17] (current) adriana.draghici [Tasks] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Lab 5. HTTP and web services ===== | ===== Lab 5. HTTP and web services ===== | ||
- | <note important>This page hasn't been updated yet for the 2021 semester and may contain outdated information</note> | ||
==== Objectives ==== | ==== Objectives ==== | ||
Line 43: | Line 42: | ||
</code> | </code> | ||
- | If order for the network request to work we need to have neccessary permissions: | + | If order for the network request to work we need to have necessary permissions: |
- | * For access to Internet | + | * For Internet access |
<code><uses-permission android:name="android.permission.INTERNET" /></code> | <code><uses-permission android:name="android.permission.INTERNET" /></code> | ||
Line 52: | Line 51: | ||
<code><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /></code> | <code><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /></code> | ||
- | Our application should not cause indirect costs to the user so that’s why we need to check what type of network connection do we need for our request. In the code snippet below we are checking that we are connected on the WiFi in order to start our file download. | ||
- | <code> | + | If your app needs to check the network's state it can use the system service ConnectivityManager - [[https://developer.android.com/training/basics/network-ops/reading-network-state|documentation examples]]. |
- | ConnectivityManager connectivityManager = | + | |
- | (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); | + | |
- | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); | + | |
- | if (networkInfo != null && networkInfo.isConnected() && | + | If your app needs to react to connectivity changes (e.g. send a file when the network becomes available) you can use Android's job scheduling API called [[https://developer.android.com/topic/libraries/architecture/workmanager|WorkManager]], since newer Android versions removed the system broadcast for connectivity changes. Here's an example (scenario #3) of how to use it: [[https://medium.com/ki-labs-engineering/monitoring-wifi-connectivity-status-part-1-c5f4287dd57|example]]. |
- | networkInfo.getType() == ConnectivityManager.TYPE_WIFI && | + | |
- | networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) { | + | |
- | downloadFileTask.execute(fileUrl); | + | |
- | } | + | |
- | </code> | + | |
- | /* THE NETWORKINFO STUFF IS DEPRECATED AND NEEDS TO BE CHANGED | ||
- | */ | ||
- | <note important>Note for Android 9 and above http calls will receive an IOException because cleartext network traffic is disabled. You can add your application trusted domains in a network security configuration file or to enable cleartext within the application manifest. Check [[https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted|Cleartext Traffic Permitted]]</note> | + | <note important>Note for Android 9 and above HTTP calls will receive an IOException because cleartext network traffic is disabled. You can add your application trusted domains in a network security configuration file or to enable cleartext within the application manifest. Check [[https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted|Cleartext Traffic Permitted]]</note> |
==== HttpsUrlConnection ==== | ==== HttpsUrlConnection ==== | ||
Line 110: | Line 98: | ||
As stated above in Retrofit we only need to declare our service interface. Below we declared our //WeatherService// which has a //GET// request for the location weather. Retrofit uses [[https://docs.oracle.com/javase/tutorial/java/annotations/|Annotations]] to show how a request will be handled. In our case we simply defined in the GET request the path to the weather endpoint and the //{location}// parameter using //{}//. In order to complete this we added the //@Path("location")// annotation before the location parameter. This way we link the method parameter to the URL parameter. | As stated above in Retrofit we only need to declare our service interface. Below we declared our //WeatherService// which has a //GET// request for the location weather. Retrofit uses [[https://docs.oracle.com/javase/tutorial/java/annotations/|Annotations]] to show how a request will be handled. In our case we simply defined in the GET request the path to the weather endpoint and the //{location}// parameter using //{}//. In order to complete this we added the //@Path("location")// annotation before the location parameter. This way we link the method parameter to the URL parameter. | ||
- | <code> | + | <coden java> |
public interface WeatherService { | public interface WeatherService { | ||
@GET("/api/weather?location={location}") | @GET("/api/weather?location={location}") | ||
Line 119: | Line 107: | ||
From here the [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Retrofit.html|Retrofit]] class generates an implementation of the WeatherService interface. When creating the Retrofit instance we need to pass our //baseUrl//. Using this and appending the requests path will create the complete URL. | From here the [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Retrofit.html|Retrofit]] class generates an implementation of the WeatherService interface. When creating the Retrofit instance we need to pass our //baseUrl//. Using this and appending the requests path will create the complete URL. | ||
- | <code> | + | <code java> |
Retrofit retrofit = new Retrofit.Builder() | Retrofit retrofit = new Retrofit.Builder() | ||
.baseUrl("https://my.weather.com/") | .baseUrl("https://my.weather.com/") | ||
Line 129: | Line 117: | ||
Using the service we can make the [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html|Call]] that will make synchronous or asynchronous HTTP request to the remote webserver. | Using the service we can make the [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html|Call]] that will make synchronous or asynchronous HTTP request to the remote webserver. | ||
- | <code> | + | <code java> |
Call<Weather> weatherCall = service.getLocationWeather("Bucharest"); | Call<Weather> weatherCall = service.getLocationWeather("Bucharest"); | ||
</code> | </code> | ||
Line 135: | Line 123: | ||
In order to make the call and get the result in our app will need to call [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html#enqueue-retrofit2.Callback-|enqueue()]]. This sends asynchronously the request and notifies the application through the //onResponse()// callback when a response is received or through //onFailure()// callback if something goes wrong. This call is handled on a background thread by Retrofit. | In order to make the call and get the result in our app will need to call [[https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html#enqueue-retrofit2.Callback-|enqueue()]]. This sends asynchronously the request and notifies the application through the //onResponse()// callback when a response is received or through //onFailure()// callback if something goes wrong. This call is handled on a background thread by Retrofit. | ||
- | <code> | + | <code java> |
weatherCall.enqueue(new Callback<Weather>() { | weatherCall.enqueue(new Callback<Weather>() { | ||
@Override | @Override | ||
Line 149: | Line 137: | ||
</code> | </code> | ||
- | If the service responds with [[https://www.json.org/|JSON]] objects, Retrofit can help deserialize them by using //converters//. A popular JSON library is [[https://github.com/google/gson|GSON]]. This helps converting JSON responses into [[https://en.wikipedia.org/wiki/Plain_old_Java_object|POJO]]s. In order to use this we need to add a [[http://square.github.io/retrofit/2.x/converter-gson/retrofit2/converter/gson/GsonConverterFactory.html|GsonConvertorFactory]] when creating the service: | + | If the service responds with [[https://www.json.org/|JSON]] objects, Retrofit can help deserialize them by using //converters//. A popular JSON library for Java is [[https://github.com/google/gson|GSON]]. If you are using Kotlin we recommend |
+ | [[https://github.com/square/moshi|Moshi]]. | ||
- | <code> | + | The GSON library helps you convert JSON responses into [[https://en.wikipedia.org/wiki/Plain_old_Java_object|POJO]]s. In order to use this we need to add a [[http://square.github.io/retrofit/2.x/converter-gson/retrofit2/converter/gson/GsonConverterFactory.html|GsonConvertorFactory]] when creating the service: |
+ | |||
+ | <code java> | ||
Retrofit retrofit = new Retrofit.Builder() | Retrofit retrofit = new Retrofit.Builder() | ||
.baseUrl("https://my.weather.com") | .baseUrl("https://my.weather.com") | ||
Line 164: | Line 155: | ||
<code> | <code> | ||
dependencies { | dependencies { | ||
- | implementation 'com.squareup.retrofit2:retrofit:2.5.0' | + | implementation 'com.squareup.retrofit2:retrofit:2.9.0' |
- | implementation "com.squareup.retrofit2:converter-gson:2.5.0" | + | implementation 'com.google.code.gson:gson:2.8.6' |
} | } | ||
</code> | </code> | ||
+ | If you plan to use Moshi, here's a Kotlin example (although Moshi supports Java too): | ||
+ | <code java> | ||
+ | val retrofit: Retrofit = Retrofit.Builder() | ||
+ | .baseUrl("https://my.weather.com") | ||
+ | .addConverterFactory(MoshiConverterFactory.create()) | ||
+ | .build() | ||
+ | </code> | ||
+ | <code> | ||
+ | apply plugin: 'kotlin-kapt' | ||
+ | |||
+ | dependencies { | ||
+ | implementation 'com.squareup.retrofit2:retrofit:2.9.0' | ||
+ | implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' | ||
+ | implementation 'com.squareup.moshi:moshi-kotlin:1.12.0' | ||
+ | |||
+ | kapt "com.squareup.moshi:moshi-kotlin-codegen:1.11.0" | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <note>Kotlin example for retrofit on our lab's repo: [[https://github.com/SMD-UPB/labs/tree/main/lab5-demos/retrofit-demo-kotlin|retrofit-demo-kotlin]]</note> | ||
==== Tasks ==== | ==== Tasks ==== | ||
=== Task 0 - Init === | === Task 0 - Init === | ||
- | Create a new Android application project. Download the lab archive and change the content of activity_main.xml and AndroidManifest.xml. | + | Create a new Android application project for API version >= Oreo. |
+ | |||
+ | Download the lab archive and replace the content of activity_main.xml and AndroidManifest.xml with the one provided in the archive. | ||
=== Task 1 - Fetch web page through HTTP (3p) === | === Task 1 - Fetch web page through HTTP (3p) === | ||
Line 192: | Line 205: | ||
Update the code from Task 1 in order to use //HttpsURLConnection//. Add the WIFI connectivity check before making the call. | Update the code from Task 1 in order to use //HttpsURLConnection//. Add the WIFI connectivity check before making the call. | ||
- | Try different websites using //http// or //https//. Are there any diferences? | + | Try different websites using //http// or //https//. Are there any differences? |
=== Task 3 - Retrofit call (4p) === | === Task 3 - Retrofit call (4p) === | ||
- | Using the code examples from the lab change the HttpURLConnection network call with Retrofit implementation. The webpage URL will be set as a base URL for the Retrofit instance. | + | Using the code examples from the lab change the HttpURLConnection network call with a Retrofit implementation. The webpage URL will be set as a base URL for the Retrofit instance. |
<note hint>Use @GET(".") for the service call.</note> | <note hint>Use @GET(".") for the service call.</note> | ||
Line 204: | Line 217: | ||
For this task will use the [[http://open-notify.org/Open-Notify-API/|Open Notify]] API which gives information about the [[https://en.wikipedia.org/wiki/International_Space_Station|ISS]]. | For this task will use the [[http://open-notify.org/Open-Notify-API/|Open Notify]] API which gives information about the [[https://en.wikipedia.org/wiki/International_Space_Station|ISS]]. | ||
- | From this API will use http://api.open-notify.org/astros.json which gives us the current number of people in space. Using the lab code examples create a Retrofit service for this URL. Make a call to get the number of people in space and return a POJO matching the JSON file structure. Make the request to the API and present in a TextView the current number of people in space. | + | From this API will use http://api.open-notify.org/astros.json which gives us the current number of people in space. Using the lab code examples create a Retrofit service for this URL. Make a call to get the number of people in space and return a POJO matching the JSON file structure. Make the request to the API and present the current number of people in space in a TextView. |
<note hint>The POJO object should contain only the attributes that we need in our application.</note> | <note hint>The POJO object should contain only the attributes that we need in our application.</note> | ||
Line 215: | Line 228: | ||
=== Resources === | === Resources === | ||
- | {{:smd:laboratoare:lab5_skel.zip|}} | + | * {{:smd:laboratoare:lab5_skel.zip|}} |
+ | * [[https://github.com/SMD-UPB/labs/tree/main/demos|Demo app for Retrofit shown during a lab]] | ||