Lab 5. HTTP and web services

Objectives

  • Learn to make secure network calls
  • Understand and practice making network calls using HttpUrlConnection, HttpsUrlConnection
  • Learn how to use an API using the Retrofit network library
  • Extra: Securing the app by integrating Firebase authentication services

HttpUrlConnection

In order to make network operations in an Android application HTTPUrlConnection is used, which is a specialization of URLConnection with HTTP capabilities. This class manages the client-server communication.

In order to use this class we need to follow these steps:

  • Create an instance of HttpURLConnection by calling openConnection(). The result needs to be casted HttpURLConnection.
  • Add information to the request besides the URI.
  • If needed, add a request body and send it using URLConnection.getOutputStream().
  • Read response from URLConnection.getInputStream().
  • Finally don’t forget to disconnect.

Here is a simple example of a using HTTPUrlConnection to get the content of a webpage into a String:

    URL url = new URL("http://example.com");
    HttpsURLConnection connection = null;
    try {
        connection = (HttpsURLConnection) url.openConnection();

        InputStream is = connection.getInputStream();
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = is.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }
        return result.toString("UTF-8");
    } catch (IOException  e) {
        Log.e(TAG, e.toString());
    } finally {
        if(connection != null) connection.disconnect();
    }

If order for the network request to work we need to have necessary permissions:

  • For Internet access
<uses-permission android:name="android.permission.INTERNET" />
  • For accessing the network state. This will help us check the connectivity so that we can initiate requests
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

If your app needs to check the network's state it can use the system service ConnectivityManager - documentation examples.

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 WorkManager, since newer Android versions removed the system broadcast for connectivity changes. Here's an example (scenario #3) of how to use it: example.

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 Cleartext Traffic Permitted

HttpsUrlConnection

For only https connections we can use HttpsURLConnection. By default HttpUrlConnection can receive use both HTTP and HTTPS because it is a superclass of HttpsUrlConnection. When we call

 HttpURLConnection connection = (HttpURLConnection) url.openConnection();

url.openConnection() will return a HttpsUrlConnetion object that can be cast to HttpUrlConnection.

From openConnection() declaration javadoc: “For example, for HTTP an HttpURLConnection will be returned, and for JAR a JarURLConnection will be returned.”

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:

  1. Add dependencies in the build.gradle file of your project (see example below)
  2. Add the INTERNET permission in the manifest file
  3. Create the classes for your model
  4. Define the requests and endpoints you need
    • create “service interfaces” and use annotations such as @GET, @POST to define the operations you need.
  5. Create a retrofit instance and provide it the service interface
  6. 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.

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.

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 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.

java
public interface WeatherService {
	@GET("/api/weather?location={location}")
	Call<Weather> getLocationWeather(@Path("location") String location);
}

From here the 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.

Retrofit retrofit = new Retrofit.Builder()
	.baseUrl("https://my.weather.com/")
	.build();
 
WeatherService service = retrofit.create(WeatherService.class);

Using the service we can make the Call that will make synchronous or asynchronous HTTP request to the remote webserver.

Call<Weather> weatherCall = service.getLocationWeather("Bucharest");

In order to make the call and get the result in our app will need to call 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.

weatherCall.enqueue(new Callback<Weather>() {
	@Override
	public void onResponse(Call<Weather> call, Response<Weather> response) {
		displayWeather(response.body());
	}
 
	@Override
	public void onFailure(Call<Weather> call, Throwable t) {
		Toast.makeText(MainActivity.this, "Something went wrong. Please try again later!", Toast.LENGTH_SHORT).show();
	}
}

If the service responds with JSON objects, Retrofit can help deserialize them by using converters. A popular JSON library for Java is GSON. If you are using Kotlin we recommend Moshi.

The GSON library helps you convert JSON responses into POJOs. In order to use this we need to add a GsonConvertorFactory when creating the service:

Retrofit retrofit = new Retrofit.Builder()
	.baseUrl("https://my.weather.com")
	.addConverterFactory(GsonConverterFactory.create())
	.build();
 
WeatherService service = retrofit.create(WeatherService.class);

In order to add the two libraries to our application we need to add them as dependencies in the app/build.gradle configuration file.

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.google.code.gson:gson:2.8.6'
}

If you plan to use Moshi, here's a Kotlin example (although Moshi supports Java too):

val retrofit: Retrofit = Retrofit.Builder()
        .baseUrl("https://my.weather.com")
        .addConverterFactory(MoshiConverterFactory.create())
        .build()
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"
}

Kotlin example for retrofit on our lab's repo: retrofit-demo-kotlin

Tasks

Task 0 - Init

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)

In this task we will download a web page content and display it.

  • Use the EditText to enter the webpage URL
  • Use the TextView to show the result
  • Use the Button to initiate the request.
  • Use HttpURLConnection to make the network request.
  • Use Runnable to do work on another thread.

Try different websites using http or https. Are there any differences?

Remove from the manifest inside the application tag the attribute android:usesCleartextTraffic=“true”. Run the app again and make the webpage request? What happens?

Task 2 - Fetch web page through HTTPS (1p)

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 differences?

Task 3 - Retrofit call (4p)

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.

Use @GET(”.”) for the service call.

Use ResponseBody as a return object from the call. In order to get the webpage content call response.body().string().

Task 4 - Retrofit call to API (2p)

For this task will use the Open Notify API which gives information about the 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 the current number of people in space in a TextView.

The POJO object should contain only the attributes that we need in our application.

Optional

Task 5 - Firebase Authentication with Email address and Password (2p)

We want to have a more secure application so that our users need to log in to see their data. In this task we will implement sign up and sign in using Firebase Authentication with Email address and Password. In order to add this to the application go to Tools→Firebase and a new window will appear. Go to Authentication and follow the steps presented there. Use the activity_onboard_layout.xml file from the lab archive as a layout for the signup/signin activity.

Resources

smd/laboratoare/05.txt · Last modified: 2021/05/18 22:17 by adriana.draghici
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