- Consuming APIs with Retrofit
- Retrofit 2 — How to Upload Files to Server
- Moonshoot
- Retrofit Series Overview
- File Upload with Retrofit 1.x
- Upload Files With Retrofit 2
- Android Client Code
- Remember the Content-Type
- Next: Upload Files with Progress Updates
- Exemplary Hapi Server for File Uploads
- Outlook
- Still Have Questions? Get Our Retrofit Book!
- Get Notified on New Future Studio Content and Platform Updates
Consuming APIs with Retrofit
Retrofit is a type-safe REST client for Android, Java and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp. See this guide to understand how OkHttp works.
This library makes downloading JSON or XML data from a web API fairly straightforward. Once the data is downloaded then it is parsed into a Plain Old Java Object (POJO) which must be defined for each «resource» in the response.
Make sure to require Internet permissions in your AndroidManifest.xml file:
Add the following to your app/build.gradle file:
Note: if you are upgrading from Retrofit 2 beta 1 or beta2, your package imports will need to be changed from import retrofit.XXXX to import retrofit2.XXXX . You will also need to update your OkHttp imports from import okhttp.XXXX to import okhttp3.XXXX because of the new OkHttp3 dependency:
If you intend to use RxJava with Retrofit 2, you will also need to include the RxJava adapter:
adapter-rxjava is now deprecated. Retrofit 2.2 and newer have a first-party call adapter for RxJava 2:(https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2)
In the past, Retrofit relied on the Gson library to serialize and deserialize JSON data. Retrofit 2 now supports many different parsers for processing network response data, including Moshi, a library build by Square for efficient JSON parsing. However, there are a few limitations, so if you are not sure which one to choose, use the Gson converter for now.
Converter | Library |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.9.0 |
Jackson | com.squareup.retrofit2:converter-jackson:2.9.0 |
Moshi | com.squareup.retrofit2:converter-moshi:2.9.0 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.9.0 |
Wire | com.squareup.retrofit2:converter-wire:2.9.0 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.9.0 |
There are two approaches discussed in this guide. The first way is the manual approach, which requires you to learn how to use the Gson library. The second approach is you can also auto-generate the Java classes you need by capturing the JSON output and using jsonschema2pojo. We encourage you to follow the manual way to best understand how the auto-generated code approach works.
See this guide about leveraging the Gson library for more information about how to create your own Java classes for use with Retrofit. The guide shows how to use Gson to ingest data from the Rotten Tomatoes API, but it can be used in the same way for any RESTful web service.
Assuming you have the JSON response already, go to jsonschema2pojo. Make sure to select JSON as the Source Type:
Set the Annotation style to Gson .
Next, paste the JSON output into the textbox:
Click the Preview button. You should see the top section look sort of like the following:
Paste the generated class into your project under a models sub-package. Rename the class name Example to reflect your model name. For this example, we will call this file and class the User model.
Note: Android does not come normally with many of the javax.annotation library by default. If you wish to keep the @Generated annotation, you will need to add this dependency. See this Stack Overflow discussion for more context. Otherwise, you can delete that annotation and use the rest of the generated code.
To send out network requests to an API, we need to use the Retrofit builder class and specify the base URL for the service.
Note also that we need to specify a factory for deserializing the response using the Gson library. The order in which the converters are added will be the sequence in which they are attempted to be processed as discussed in this video talk. If we wish to pass in a custom Gson parser instance, it can be specified too:
With Retrofit 2, endpoints are defined inside of an interface using special retrofit annotations to encode details about the parameters and request method. In addition, the return value is always a parameterized Call object such as Call . If you do not need any type-specific response, you can specify return value as simply Call .
For instance, the interface defines each endpoint in the following way:
Notice that each endpoint specifies an annotation of the HTTP method (GET, POST, etc.) and method that will be used to dispatch the network call. Note that the parameters of this method can also have special annotations:
Annotation | Description |
---|---|
@Path | variable substitution for the API endpoint (i.e. username will be swapped for |
@Query | specifies the query key name with the value of the annotated parameter. |
@Body | payload for the POST call (serialized from a Java object to a JSON string) |
@Header | specifies the header with the value of the annotated parameter |
Normally, the base URL is defined when you instantiated an Retrofit instance. Retrofit 2 allows you to override the base URL specified by changing it in the annotation (i.e. if you need to keep one specific endpoint using an older API endpoint)
There are also others that allow you to modify the base URL using relative paths (and not the fully qualified URL) as discussed in this blog article.
Notice that there is a @Headers and @Header annotation. The Headers can be used to provide pre-defined ones:
We can also add headers as a parameter to the endpoint:
If we wish to submit form-encoded data, we can use the FormUrlEncoded annotation. The @Field annotation will denote what payload will be submitted as form data.
If we need to upload images or files, we need to send by using Multipart forms. We will to mark the endpoint with @Multipart , and label at least one parameter with @Part.
Assuming we have a reference to the file, we can create a RequestBody object:
If you need to specify a unique filename for your multipart upload, there is currently an issue in Retrofit 2 tracked in this ticket. Alternatively, you can create a multi-part RequestBody according to this OkHttp recipe guide and pass it along as one of the @Part annotated parameters:
If we wish to POST form-encoded name/value pairs, we can use the @FormUrlEncoded and @FieldMap annotations:
Retrofit 2 will use the converter library chosen to handle the deserialization of data from a Java object. If you annotate the parameter with a @Body parameter, this work will be done automatically. If you are using the Gson library for instance, any field belonging to the class will be serialized for you. You can change this name using the @SerializedName decorator:
Our endpoint would look like the following:
We could invoke this API call as follows:
The resulting network call would POST this data:
If you are trying to upgrade from Retrofit 1, you may remember that the last parameter had to be a Callback type if you wanted to define the API call to run asynchronously instead of synchronously:
Retrofit 1 relied on this Callback type as a last parameter to determine whether the API call would be dispatched asynchronously instead of synchronously. To avoid having two different calling patterns, this interface was consolidated in Retrofit 2. You now simply define the return value with a parameterized Call , as shown in the previous section.
We can bring this all together by constructing a service leveraging the MyApiEndpointInterface interface with the defined endpoints:
If we want to consume the API asynchronously, we call the service as follows:
Shown above, Retrofit will download and parse the API data on a background thread, and then deliver the results back to the UI thread via the onResponse or onFailure method.
Note also that OkHttp, which dispatches the callback on the worker thread, callbacks in Retrofit are dispatched on the main thread. Because UI updates can only be done on the main thread, the approach used by Retrofit can make it easier to make changes to your views.
If you are using Retrofit in a Background Service instead of an Activity or Fragment, you can run the network call synchronously within the same thread by using the execute() method.
Retrofit 2 also supports RxJava extensions. You will need to create an RxJava Adapter. By default, all network calls are synchronous:
If you wish to default network calls to be asynchronous, you need to use createWithScheduler() .
You can then instantiate the Retrofit instance:
Instead of creating Call objects, we will use Observable types.
Consistent with the RxJava framework, we need to create a subscriber to handle the response. The methods onCompleted() , onError() , and onNext() need to be added. Using the Android RxJava library, we can also designate that we will handle this event on the UI main thread. Note: If you intend to override the default network call behavior, you can specify subscribeOn() . Otherwise, it can omitted. Note: As the RxJava rewrote their API, the term «Subscription» used here shall be replaced with «Disposable». As the word «Subscription» had conflict intentions.
Note that if you are running any API calls in an activity or fragment, you will need to unsubscribe on the onDestroy() method. The reasons are explained in this Wiki article:
Note also that subscribing an observer to the observable is what triggers the network request. For more information about how to attach multiple observers before dispatching the network requests, see this section.
Headers can be added to a request using an Interceptor . To send requests to an authenticated API, add headers to your requests using an interceptor as outlined below:
Notice that in Retrofit 2 the interceptor has to be added to a custom OkHttpClient . In Retrofit 1, it could be set directly by the builder class.
In order to authenticate with OAuth, we need to sign each network request sent out with a special header that embeds the access token for the user that is obtained during the OAuth process. The actual OAuth process needs to be completed with a third-party library such as signpost and then the access token needs to be added to the header using a request interceptor. Relevant links for Retrofit and authentication below:
Resources for using signpost to authenticate with an OAuth API:
Several other Android OAuth libraries can be explored instead of signpost:
There is a known issue currently with Retrofit 2 passing Lint tests tracked in this ticket. In particular, you may see package reference in library; not included in Android: java.nio.file. Referenced from okio.Okio. or Invalid package reference in library; not included in Android: java.lang.invoke. Referenced from retrofit.Platform.Java8. .
To bypass this issue, create a gradle/lint.xml in your root project:
Add this line to your app/build.gradle file:
The lint errors should be suppressed and not trigger any additional errors for now.
Retrofit and OkHttp can be hard to troubleshoot when trying to step through the various layers of abstraction in the libraries. You can add the HttpLogInterceptor that can be added when using the OkHttp3 library, which will print HTTP requests/responses through LogCat. You can also leverage Facebook’s Stetho project to use Chrome to inspect all network traffic.
To use HttpLogInterceptor , add this dependency to your Gradle configuration:
You will need to add a network interceptor for HttpLogInterceptor. See this doc for the different options that can be used.
You then need to pass this custom OkHttpClient into the Retrofit builder:
Facebook’s Stetho project enables you to use Chrome debugging tools to troubleshoot network traffic, database files, and view layouts. See this guide for more details.
Источник
Retrofit 2 — How to Upload Files to Server
Moonshoot
Moonshoot is a
Student Feature.
The previous tutorials guided you through various use cases of Retrofit and showed you opportunities to enhance the app with Retrofit’s built-in functionality. This tutorial will show you how to upload a file to a backend server using the second major release of Retrofit, namely Retrofit 2.
Retrofit Series Overview
Getting Started and Creating an Android Client
Basics of API Description
Creating a Sustainable Android Client
URL Handling, Resolution and Parsing
How to Change API Base Url at Runtime
Multiple Server Environments (Develop, Staging, Production)
Share OkHttp Client and Converters between Retrofit Instances
Upgrade Guide from 1.9
Beyond Android: Retrofit for Java Projects
How to use OkHttp 3 with Retrofit 1
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
Synchronous and Asynchronous Requests
Send Objects in Request Body
Add Custom Request Header
Manage Request Headers in OkHttp Interceptor
Dynamic Request Headers with @HeaderMap
Multiple Query Parameters of Same Name
Optional Query Parameters
Send Data Form-Urlencoded
Send Data Form-Urlencoded Using FieldMap
How to Add Query Parameters to Every Request
Add Multiple Query Parameter With QueryMap
How to Use Dynamic Urls for Requests
Constant, Default and Logic Values for POST and PUT Requests
Reuse and Analyze Requests
Optional Path Parameters
How to Send Plain Text Request Body
Customize Network Timeouts
How to Trust Unsafe SSL certificates (Self-signed, Expired)
Dynamic Endpoint-Dependent Interceptor Actions
How to Update Objects on the Server (PUT vs. PATCH)
How to Delete Objects on the Server
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
Ignore Response Payload with Call
Receive Plain-String Responses
Crawl HTML Responses with jspoon (Wikipedia Example)
Loading Data into RecyclerView and CardView
- Ignore Response Payload with Call
- Receive Plain-String Responses
- Crawl HTML Responses with jspoon (Wikipedia Example)
- Loading Data into RecyclerView and CardView
Introduction to (Multiple) Converters
Adding & Customizing the Gson Converter
Implementing Custom Converters
How to Integrate XML Converter
Access Mapped Objects and Raw Response Payload
Supporting JSON and XML Responses Concurrently
Handling of Empty Server Responses with Custom Converter
Send JSON Requests and Receive XML Responses (or vice versa)
Unwrapping Envelope Responses with Custom Converter
Wrapping Requests in Envelope with Custom Converter
Define a Custom Response Converter
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
Simple Error Handling
Error Handling for Synchronous Requests
Catch Server Errors Globally with Response Interceptor
How to Detect Network and Conversion Errors in onFailure
Log Requests and Responses
Enable Logging for Development Builds Only
Log Network Traffic with Stetho and Chrome Developer Tools
Using the Log Level to Debug Requests
Analyze Network Traffic with Android Studio Profiler
Debug and Compare Requests with RequestBin
- Log Requests and Responses
- Enable Logging for Development Builds Only
- Log Network Traffic with Stetho and Chrome Developer Tools
- Using the Log Level to Debug Requests
- Analyze Network Traffic with Android Studio Profiler
- Debug and Compare Requests with RequestBin
Introduction to Call Adapters
Custom Call Adapter to Separate OnResponse Callback
How to Integrate RxJava 1.x Call Adapter
How to Integrate RxJava 2.x Call Adapter
How to Integrate Guava Call Adapter
Custom Call Adapter to Separate Network and Gson Errors
- Introduction to Call Adapters
- Custom Call Adapter to Separate OnResponse Callback
- How to Integrate RxJava 1.x Call Adapter
- How to Integrate RxJava 2.x Call Adapter
- How to Integrate Guava Call Adapter
- Custom Call Adapter to Separate Network and Gson Errors
Pagination Using Query Parameter
Pagination Using Link Header and Dynamic Urls (Like GitHub)
Pagination Using Range Header Fields (Like Heroku)
- Pagination Using Query Parameter
- Pagination Using Link Header and Dynamic Urls (Like GitHub)
- Pagination Using Range Header Fields (Like Heroku)
How to Upload Files to Server
How to Upload Multiple Files to Server
How to Upload a Dynamic Amount of Files to Server
Upload Files with Progress
Passing Multiple Parts Along a File with @PartMap
How to Download Files from Server
Download Files with Progress Updates
How to Upload Files to Server
- How to Upload Files to Server
- How to Upload Multiple Files to Server
- How to Upload a Dynamic Amount of Files to Server
- Upload Files with Progress
- Passing Multiple Parts Along a File with @PartMap
- How to Download Files from Server
- Download Files with Progress Updates
- How to Upload Files to Server
Basic Authentication on Android
Token Authentication on Android
OAuth on Android
Hawk Authentication on Android
How to Refresh an Access Token
- Basic Authentication on Android
- Token Authentication on Android
- OAuth on Android
- Hawk Authentication on Android
- How to Refresh an Access Token
Activate Response Caching (Etag, Last-Modified)
Check Response Origin (Network, Cache, or Both)
Force Server Cache Support with Response Interceptor
Support App Offline Mode by Accessing Response Caches
Analyze Cache Files
- Activate Response Caching (Etag, Last-Modified)
- Check Response Origin (Network, Cache, or Both)
- Force Server Cache Support with Response Interceptor
- Support App Offline Mode by Accessing Response Caches
- Analyze Cache Files
Basics of Mocking Server Responses
Customizing Network Behavior of Mocked Server Responses
Mock APIs with JsonServer
- Basics of Mocking Server Responses
- Customizing Network Behavior of Mocked Server Responses
- Mock APIs with JsonServer
Fluent Interface with Builders
File Upload with Retrofit 1.x
We’ve already published a tutorial on how to upload files using Retrofit 1.x . If you’re using Retrofit 1, please follow the link.
Using Retrofit 2? This tutorial is for you and please read on 🙂
Upload Files With Retrofit 2
This tutorial is intentionally separated from the already published tutorial on how to upload files with Retrofit v1, because the internal changes from Retrofit 1 to Retrofit 2 are profound and you need to understand the way Retrofit 2 handles file uploads.
Before we dive deeper into the file upload topic with Retrofit 2, let’s shortly recap the previously used functionality in v1. Retrofit 1 used a class called TypedFile for file uploads to a server. This class has been removed from Retrofit 2. Further, Retrofit 2 now leverages the OkHttp library for any network operation and, as a result, OkHttp’s classes for use cases like file uploads.
Using Retrofit 2, you need to use either OkHttp’s RequestBody or MultipartBody.Part classes and encapsulate your file into a request body. Let’s have a look at the interface definition for file uploads.
Let me explain each part of the definition above. First, you need to declare the entire call as @Multipart request. Let’s continue with the annotation for description . The description is just a string value wrapped within a RequestBody instance. Secondly, there’s another @Part within the request: the actual file . We use the MultipartBody.Part class that allows us to send the actual file name besides the binary file data with the request. You’ll see how to create the file object correctly within the following section.
Android Client Code
At this point, you’ve defined the necessary service interface for Retrofit. Now you can move on and touch the actual file upload. We’ll use the ServiceGenerator class which generates a service client. We’ve introduced the ServiceGenerator class in the creating a sustainable Android client tutorial earlier in this series.
The following code snippet shows the uploadFile(Uri fileUri) method taking the file’s uri as a parameter. If you’re starting an intent to choose a file, you’ll return within the onActivityResult() method of Android’s lifecycle. In this method, you can get the file’s uri and that’s exactly what you’ll use to upload the file within the uploadFile method.
The snippet above shows you the code to initialize the payload ( body and description ) and how to use the file upload service. As already mentioned, the RequestBody class is from OkHttp and used for the description. Its .create() method requires two parameters: first, the media type, and second, the actual data. The media type for the description can simply be OkHttp’s constant for multipart requests: okhttp3.MultipartBody.FORM . The media type for the file should ideally be the actual content-type. For example, a PNG image should have image/png . Our code snippet above figures out the content type with the getContentResolver().getType(fileUri) call and then parses the result into OkHttp’s media type with MediaType.parse() .
Besides the description, you’ll add the file wrapped into a MultipartBody.Part instance. That’s what you need to use to appropriately upload files from client-side. Further, you can add the original file name within the createFormData() method and reuse it on your backend.
Remember the Content-Type
Please keep an eye on Retrofit’s content type. If you intercept the underlying OkHttp client and change the content type to application/json , your server might have issues with the deserialization process. Make sure you’re not defining the header indicating you’re sending JSON data, but multipart/form-data .
Next: Upload Files with Progress Updates
If you upload files in the foreground and they are not small, you might want to inform the user on your actions. Ideally, you would display progress updates how much you’ve uploaded already. We’ve another tutorial on how to upload files with progress updates.
Exemplary Hapi Server for File Uploads
If you already have your backend project, you can lean on the example code below. We use a simple hapi server with a POST route available at /upload . Additionally, we tell hapi to don’t parse the incoming request, because we use a Node.js library called multiparty for the payload parsing.
Within the callback of multiparty’s parsing function, we’re logging each field to show its output.
Android client expects a return type of String , we’re sending the received information as response. Of course your response will and should look different 🙂
Below you can see the output of a successful request and payload parsing on server-side. The first null is the err object. Afterwards, you can see the fields which is only the description as part of the request. And last but not least, the file is available within the picture field. Here you see our previously defined names on client side. 20160312_095248.jpg is passed as the original name and the actual field name is picture . For further processing, access the uploaded image at path ’s location.
Server Log for Parsed Payload
Outlook
File uploads are an essential feature within up-to-date apps and you can integrate this feature within your app using Retrofit. This tutorial guided you through the necessary steps to upload a file from your Android device to your backend server.
What to expect within the next post on Retrofit? Next week you’ll learn all about how to get back logging within Retrofit 2. Stay tuned, it will be a good shot!
Still Have Questions? Get Our Retrofit Book!
All modern Android apps need to do network requests. Retrofit offers you an extremely convenient way of creating and managing network requests. From asynchronous execution on a background thread, to automatic conversion of server responses to Java objects, Retrofit does almost everything for you. Once you’ve a deep understanding of Retrofit, writing complex requests (e.g., OAuth authentication) will be done in a few minutes.
Invest time to fully understand Retrofit’s principles. It’ll pay off multiple times in the future! Our book offers you a fast and easy way to get a full overview over Retrofit. You’ll learn how to create effective REST clients on Android in every detail.
Boost your productivity and enjoy working with complex APIs.
Get Notified on New Future Studio
Content and Platform Updates
Get your weekly push notification about new and trending
Future Studio content and recent platform enhancements
Источник