- REST API on Android Made Simple or: How I Learned to Stop Worrying and Love the RxJava
- Part I: Making API calls with Retrofit
- Part II: Handling asynchronicity and more with RxJava
- Android для начинающих: использование REST API
- 1. Включение доступа к Интернету
- 2. Создание фоновых потоков
- 3. Создание HTTP-соединения
- 4. Добавление заголовков запросов
- 5. Чтение ответов
- 6. Разбор JSON ответов
- 7. Использование разных HTTP методов
- 8. Кэширование ответов
- Заключение
REST API on Android Made Simple or: How I Learned to Stop Worrying and Love the RxJava
May 29, 2018 · 10 min read
If you’re an Android developer you probably remember, with no small amount of nostalgia, the first time you encountered the NetworkingOnMainThread exception. Asynchronicity is one of the main driving forces behind the Android platform, and yet, the libraries provided by the SDK always were somewhat lacking when it comes to dealing with it. Combine that with tedious work of manually dealing with any kind of RESTful API, and you soon find yourself in a hell made of disjointed code fragments, repeated loops and confusing callbacks.
This article wi l l show you, step-by-step, how to make a simple API call on Android the easy way — using 3rd party libraries. In the first part we’ll deal with the API call itself using Retrofit and Gson. In the second part we’ll see how we can make our lives even simpler and deal with asynchronicity in a concise and elegant way using RxJava.
Disclaimer — This article does not cover the subject of RESTful APIs in great detail. It is not an in-depth Retrofit or RxJava tutorial. Rather, it’s a short and simple guide on dealing with an API request using said two libraries in conjunction.
Part I: Making API calls with Retrofit
For this part we will be using Retrofit2 and it’s Gson adapter so let’s start by adding their respective dependencies to our build.gradle file (also add the RxJava dependencies, as we’ll need them later):
By the way, let’s not forget to add the Internet permission to our manifest file:
Retrofit makes it really easy to consume APIs by doing a lot of heavy lifting for us. It won’t do all the work by itself though, so let’s see what we have to do to get things rolling:
We use builder pattern to configure and build a Retrofit instance. For more details about Retrofit configuration see the documentation, for now just make sure to include API’s base URL, GsonConverterFactory and RxJava2CallAdapterFactory. These are necessary if we want to use Retrofit with Gson and RxJava, as we will be doing in this example.
If you’re more familiar with some other JSON parsing library or just don’t feel like using Gson you can check out the list of supported converters here.
2. Create a model class
Gson uses model (or POJO) classes to convert JSON data to objects. One thing to remember when it comes to model classes is this: they have to reflect the structure of JSON response we want them to represent. So let’s say we want to get some data about a famous movie director from the popular and free-to-use The Movie Database API — if we consult the API documentation, we can see that our JSON response would look something like this (shortened for the sake of simplicity):
We could write the model class by hand but there are many convenient converter tools to save us some precious time, for example this one. Copy the response code, paste it into the converter, select Gson, click on preview and, if all went well, what comes out should very much resemble a simple POJO class:
As we can see, the fields represent our JSON response. Annotations are not necessary for Gson to work but they can come in handy if we want to be specific or keep the camel case naming. Just keep in mind that if a variable name doesn’t match the JSON key of the corresponding key/value pair Gson won’t recognize it and it will be ignored. That’s where the @SerializedName comes in — as long as we specify it correctly we can name the variable however we want. And that’s that for the model class. Let’s see how to actually make use of Retrofit to specify our requests.
3. Create an ApiService interface
To use Retrofit we simply make an interface to house our API requests (name of the interface is arbitrary, but it’s a good practice to end it with service). Then we use annotations from the Retrofit library to annotate interface methods with specifics of the each request. Going back to the API documentation, we find that our request should look something like this:
Let’s take a closer look at this piece of code and see what it means:
- @GET tells Retrofit what kind of request this is, followed by the coresponding API endpoint. It should be obtained from the API documentation
- Single
is an RxJava constuct and we’ll deal with it later, for now just keep in mind that it requires a type, which must be of the model class we are fetching with the request (in this case, Person)
Put simply, when this method is called, Retrofit will generate a request and forward it to the API. If successful, it will use the Gson converter to convert the response to the object of our model class. Under the hood, the generated request looks roughly like this:
For those still uncertain about what goes where, here is a quick recap:
- https://api.themoviedb.org/3/ — this is the base URL
- person/ — this is the endpoint followed by a path
- ? — a question mark indicates the beginning of queries
- api_key= — this is a query, in this case, for an api key
- & — more queries incoming
- language=en-US — this too is a query, but we left it at default
And since we annotated the parameters with @Path(“person_id”) and @Query(“api_key”), the arguments we pass when calling the method will replace and .
4. Hook up the service with Retrofit and make the request
All that’s left to do is to actually make the request. To do that we must first create an instance of the ApiService using the Retrofit object we created at the beginning.
That last line of code is where we actually make a request to get the data we need, wrapped in a Single object. But what is a Single? And how does it help us deal with the asynchronous nature of network calls? We answer these questions, and more, in the second part.
Part II: Handling asynchronicity and more with RxJava
We’ve all heard of AsyncTask. Most of us have used it at some point in our developer careers. The unfortunate few still use it today. But almost everybody hates it. It’s overly verbose, clumsy, prone to memory leaks and bugs, and yet, in lack of a better tool, many online tutorials still use it in dealing with all that background work Android platform is ripe with.
In this part we’ll see how to ditch the AsyncTask altogether in favor of RxJava, and in the process, save yourself a lot of time and avoid headache. Keep in mind that this is not an RxJava tutorial and we will only scratch the surface of what RxJava is capable of — working with Retrofit to ensure a simple and elegant solution to asynchronous network calls.
In the last part we left off at this line of code:
So what is a Single? Without making this an RxJava tutorial, let’s say it allows us to recieve a single set of data from the API, do some stuff with it in the background, and, when done, present it to the user — all that in a few lines of code. Internally, it is based on the observer pattern and some functional programming goodness, with data being pushed to interested observers at the moment of subscription.
To receive the data we only have to subscribe to a Single, calling the subscribe() method on it and passing a SingleObserver as an argument. SingleObserver is an interface containing 3 methods. By subscribing we ensure that the data is pushed when ready, and is passed to us in onSuccess() method. That is, if request was successfully completed — if not, onError() is invoked, enabling us to deal with the exception as we see fit.
But what about the onSubscribe() method? It is called in the moment of subscription and it can serve us to prevent potential memory leaks. It gives us access to a Disposable object, which is just a fancy name for the reference to the connection we established between our Single and a SingleObserver — the subscription. That subscription can be disposed with a simple method call, thus preventing those nasty situations when, for example, rotating the device in the middle of a running background task causes a memory leak. What we want to do is the following:
- first we create a CompositeDisposable object which acts as a container for disposables (think Recycle Bin) and add our Disposable to it in the onSubscribe() method:
- then we simply call the dispose() method on the CompositeDisposable in the appropriate lifecycle method:
We’re almost there — we got the data we wanted and we did our chores of cleaning up any potential mess. But we still haven’t dealt with asynchronicity. NetworkingOnMainThread exception will still be thrown. Now comes the hard part, you must be thinking? Not really. Thanks to the RxAndroid library, RxJava can be made aware of Android threads, so all we have to do is add two more lines of code:
As easy as that, with subscribeOn() we told RxJava to do all the work on the background(io) thread. When the work is done and our data is ready, observeOn() ensures that onSuccess() or onError() are called on the main thread.
While we’re at it, let’s explore RxJava some more. Let’s say, for the sake of example, that we want to make some more use of the API and fetch a list of movies related to our Person. We would have to make another API request following the same steps — create a model class and add another method to the ApiService, with the corresponding API endpoint. You’re probably thinking we have to repeat the steps with RxJava too, then think of a complex logic to deal with acychronicity and time the callbacks so we can update the UI at just the right time? Not necessarily.
Enter RxJava Operators. Operators are methods that allow us to, well, operate on our data and manipulate the way it’s being pushed to us. They can be called on Single objects and chained just like we did before. The list of available operators and their uses is huge and you can find it here.
In our case, wouldn’t it be nice if we could simply join the Person with the list of movies in the background and get them at the same time, as a single set of data? Luckily, there is just the Operator to meet our needs — Zip. Presuming the request for movies returns a list of Movie objects, we can add a List field to the Person class and do the following:
If it seems confusing at first glance, don’t worry, it usually is. It takes some time and practice to get acquainted with the syntax, and knowing your lambda expressions helps a lot. What we did is call the zipWith() method on the Single wrapping a Person request — zipWith() accepts 2 arguments — another Single (the one wrapping a list of movies) and a lambda expression which gives us access to both person and movie list data, since the Zip operator joins two separate data sources. In the lambda we assigned the movie list to the person. Now, when all the background work is done, onSuccess() will give us a Person object together with the list of movies we assigned to it.
Источник
Android для начинающих: использование REST API
Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)
Большинство из нас стали весьма жадны до новой информации, что в Интернете является такой важной частью нашей жизни. Поэтому создание приложений Android со статичным контентом может быть плохой идеей. Вместо этого вам следует рассмотреть возможность создания приложений, которые могут отображать свежий контент каждый раз, когда пользователь их открывает.
Это может звучать сложно, но с большим количеством сайтов, которые раскрывают свои ресурсы через REST API, на самом деле это довольно просто. (Смотрите руководство для начинающих по HTTP и REST для примера.)
В этом уроке я расскажу вам, как использовать классы и методы, доступные в Android SDK, для подключения к удаленным веб-серверам и взаимодействия с ними с использованием их REST API.
1. Включение доступа к Интернету
Использование REST API, очевидно, связано с использованием Интернета. Тем не менее, приложения Android могут получить доступ к Интернету только в том случае, если у них есть разрешение android.permission.INTERNET . Поэтому перед началом написания любого сетевого кода вы должны убедиться, что в файле манифеста вашего проекта присутствуют следующие uses-permission теги:
Поскольку android.permission.INTERNET не считается опасным разрешением, вам не нужно запрашивать его во время выполнения на устройствах с уровнем API 23 или выше.
2. Создание фоновых потоков
Платформа Android не позволяет выполнять сетевые операции в основном потоке приложения. Поэтому весь ваш сетевой код должен принадлежать фоновому потоку. Самый простой способ создать такой поток — использовать метод execute() класса AsyncTask . В качестве единственного аргумента execute() ожидает объект Runnable .
Если вы хотите узнать больше о выполнении операций в фоновом потоке, я предлагаю вам прочитать этот учебник о фоновых операциях из серии Android для начинающих.
3. Создание HTTP-соединения
Используя метод openConnection() класса URL , вы можете быстро настроить соединение с любой конечной точкой REST. Возвращаемое значение openConnection() должно быть передано в экземпляр HttpURLConnection или HttpsURLConnection , в зависимости от доступа к конечной точке через HTTP или HTTPS. Оба HttpURLConnection и HttpsURLConnection позволяют выполнять такие операции, как добавление заголовков запросов и чтение ответов.
В следующем фрагменте кода показано, как настроить соединение с корневой конечной точкой API GitHub:
Обратите внимание, что HttpsURLConnection является подклассом класса HttpURLConnection .
4. Добавление заголовков запросов
Большинство веб-сайтов, предлагающих REST API, хотят иметь возможность однозначно идентифицировать ваше приложение. Самый простой способ помочь им сделать это — включить уникальный заголовок User-Agent во все ваши запросы.
Чтобы добавить заголовок User-Agent в ваш запрос, вы должны использовать метод setRequestProperty() объекта HttpURLConnection . Например, вот как вы устанавливаете заголовок User-Agent в my-rest-app-v0.1:
Вы можете добавить несколько заголовков к своему запросу, вызвав несколько раз метод setRequestProperty() . Например, следующий фрагмент кода добавляет заголовок Accept и кастомный заголовок Contact-Me :
5. Чтение ответов
После того как вы передали все заголовки запросов, вы можете проверить, есть ли у вас валидный ответ, используя метод getResponseCode() объекта HttpURLConnection .
Если класс HttpURLConnection получает код ответа на перенаправление, например 301, он автоматически обрабатывает его и следует за перенаправлением. Поэтому, как правило, вам не нужно будет писать дополнительный код для проверки перенаправления.
В случае отсутствия ошибок вы можете теперь вызвать метод getInputStream() , чтобы получить ссылку на входящий поток соединения.
Большинство REST API в наши дни возвращают данные, отформатированные как документы JSON. Поэтому, вместо прямого чтения из объекта InputStream , я предлагаю вам создать для него InputStreamReader .
6. Разбор JSON ответов
Android SDK имеет класс JsonReader, который позволяет легко разбирать документы JSON. Вы можете создать новый экземпляр класса JsonReader , передав объект InputStreamReader его конструктору.
То как вы извлекаете определенную часть информации из документа JSON, зависит от его структуры. Например, документ JSON, возвращаемый корневой конечной точкой REST API GitHub, выглядит следующим образом:
Как вы можете видеть, ответ — это только один большой объект JSON, содержащий несколько ключей. Чтобы извлечь из него значение с именем organization_url, вам нужно будет написать следующий код:
Вышеупомянутый код обрабатывает ответ JSON как поток токенов. Поэтому он потребляет очень мало памяти. Однако, поскольку он должен обрабатывать каждый отдельный токен один за другим, он может оказаться медленным при обработке больших ответов.
После того как вы извлечете всю необходимую информацию, вы всегда должны вызвать метод close() для объекта JsonReader , чтобы он освобождал все сохраненные ресурсы.
Вы также должны закрыть соединение, вызвав метод disconnect() объекта HttpURLConnection .
7. Использование разных HTTP методов
HTTP-интерфейсы REST используют методы HTTP для определения типа операции, которая должна выполняться над ресурсом. На предыдущих шагах мы использовали метод HTTP GET для выполнения операции чтения. Поскольку класс HttpURLConnection использует по умолчанию метод GET , нам не нужно было его явно указывать.
Чтобы изменить метод HTTP вашего объекта HttpURLConnection , вы должны использовать его метод setRequestMethod() . Например, следующий фрагмент кода открывает соединение с конечной точкой, принадлежащей httpbin.org, и устанавливает свой HTTP-метод в POST :
Как вы уже знаете, POST -запросы используются для отправки данных на сервер. При записи в выходной поток соединения вы можете легко добавить любые данные в тело запроса POST . Однако, прежде чем вы это сделаете, вы должны убедиться, что вы вызываете метод setDoOutput() объекта HttpURLConnection и передаете ему значение true .
В следующем фрагменте кода показано, как отправить на сервер простую пару «ключ-значение»:
8. Кэширование ответов
Всегда рекомендуется кэшировать ответы HTTP. Таким образом, вы можете не только сократить потребление пропускной способности вашего приложения, но и сделать его более отзывчивым. Начиная с уровня API 13, Android SDK предлагает класс HttpResponseCache , который позволяет легко реализовать кэширование без каких-либо изменений в вашей сетевой логике.
Чтобы установить кэш для вашего приложения, вы должны вызвать метод install() класса HttpResponseCache . Метод ожидает абсолютный путь, указывающий, где должен быть установлен кеш, и число, определяющее размер кеша. Вы можете использовать метод getCacheDir() , если вы не хотите указывать абсолютный путь вручную.
В следующем фрагменте кода устанавливается кеш размером 100 000 байт:
Как только кеш установлен, класс HttpURLConnection начинает использовать его автоматически. Чтобы проверить, работает ли ваш кеш, вы можете использовать его метод getHitCount() , который возвращает количество HTTP-ответов, которые были отправлены из кеша.
Заключение
Существуют тысячи REST API-интерфейсов, которые вы можете свободно использовать в своих приложениях для Android. Используя их, вы можете сделать ваше приложение более информативным, интересным и многофункциональным. В этом уроке вы узнали, как использовать класс HttpURLConnection для использования таких REST API. Вы также узнали, как создать кеш ответов HTTP, который снижает использование потребление сетевого трафика вашим приложением.
Если вы считаете, что использование HttpURLConnection слишком сложное, вам следует обратить внимание на сторонние библиотеки, такие как например, Volley. Библиотеки, подобные этой, используют класс HttpURLConnection внутри, но предоставляют множество удобных методов, которые позволяют сделать ваш код более кратким и читаемым.
Чтобы узнать больше о работе с сетью на платформе Android, вы можете обратиться к руководству по сетевым операциям Android.
Источник