Differences

This shows you the differences between two versions of the page.

Link to this comparison view

smd:laboratoare:05 [2019/04/01 19:55]
vasile.cosovanu [Tasks]
smd:laboratoare:05 [2021/05/18 22:17] (current)
adriana.draghici [Tasks]
Line 42: 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 51: 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)+If your app needs to react to connectivity changes ​(e.g. send a file when the network becomes availableyou 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 #3of how to use it: [[https://​medium.com/​ki-labs-engineering/​monitoring-wifi-connectivity-status-part-1-c5f4287dd57|example]].
-    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();+
  
-    if (networkInfo != null && networkInfo.isConnected() && 
-            networkInfo.getType() == ConnectivityManager.TYPE_WIFI && ​ 
-            networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) { 
-        downloadFileTask.execute(fileUrl);​ 
-    } 
-</​code>​ 
  
-<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 81: Line 73:
  
 [[https://​square.github.io/​retrofit/​|Retrofit]] is a type-safe HTTP Client for Android and Java. The basic functionality it is to turn the HTTP API into a Java interface. ​ [[https://​square.github.io/​retrofit/​|Retrofit]] is a type-safe HTTP Client for Android and Java. The basic functionality it is to turn the HTTP API into a Java interface. ​
 +
 +When working with retrofit you need to consider the following:
 +  * How is the data transmitted. For example, one popular way is to use JSON objects. Retrofit can handle their conversion to Java objects
 +  * A **model** for the data you need transmitted
 +    * this is usually a class with fields that reflect the JSON schema you use in your requests
 +  * Define the endpoints and the requests you need to make in an interface, we refer to it as **service** (not to be confused with Android services)
 + 
 +
 +Basic steps for using this library:
 +  - Add dependencies in the build.gradle file of your project (see example below)
 +  - Add the INTERNET permission in the manifest file
 +  - Create the classes for your model
 +  - Define the requests and endpoints you need
 +     * create "​service interfaces"​ and use [[https://​futurestud.io/​tutorials/​java-basics-for-retrofit-annotations|annotations]] such as @GET, @POST to define the operations you need.
 +  - Create a retrofit instance and provide it the service interface
 +  - Make calls to the api by calling the service'​s methods.
 +     * provide callbacks to the calls in order to react to the results or failures
 +
  
 In this section'​s code examples we're going to create a basic HTTP GET request for a weather API service. This will receive //​location//​ as a parameter and will return a Weather object that has all the data related to the current weather for that location. In this section'​s code examples we're going to create a basic HTTP GET request for a weather API service. This will receive //​location//​ as a parameter and will return a Weather object that has all the data related to the current weather for that location.
 +
 +<note important>​When using Retrofit, there is no need to run the HTTP call using Retrofit in a separate thread. Retrofit already does this, creating threads for requests so that the user doesn'​t have to do this.</​note>​
  
 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 95: 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 105: 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 111: 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 125: Line 137:
 </​code>​ </​code>​
  
-If the service responds with [[https://​www.json.org/​|JSON]] objects, Retrofit can help deserialize them by using //​converters//​. A populr ​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]]sIn 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 140: 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.+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 158: Line 195:
   * Use the Button to initiate the request. ​   * Use the Button to initiate the request. ​
   * Use HttpURLConnection to make the network request. ​   * Use HttpURLConnection to make the network request. ​
-  * Use AsyncTask/Runnable to do work on another thread.+  * Use Runnable to do work on another thread.
  
 Try different websites using //http// or //https//. Are there any differences? ​ Try different websites using //http// or //https//. Are there any differences? ​
Line 168: 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 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 180: 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 191: 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]]
  
  
smd/laboratoare/05.1554137743.txt.gz · Last modified: 2019/04/01 19:55 by vasile.cosovanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0