Если вы хотите использовать gRPC в своем приложении React Native, официальной поддержки пока нет, но это вас не должно останавливать! В этом посте я покажу вам, как мы разработали реализацию с учетом безопасности типов и успешно вызвали службу удаленно из React Native на Android.

Обоснование

На предыдущем Дне инноваций Xebia я имел удовольствие объединиться для реализации микросервисной архитектуры с использованием gRPC. gRPC - это среда удаленного вызова процедур (как следует из названия), которая ориентирована как на высокую производительность, так и на доступность на наиболее распространенных языках программирования. Он использует буферы протокола, двоичный формат для передачи сообщений, который обычно передается через стек HTTP2.

Моей личной целью было создать привязку Android React Native для одной из потребляющих клиентских служб в нашей архитектуре. Одно из возможных решений, которое мы рассмотрели, заключалось в использовании компилятора protoc, который поставляется с платформой gRPC, для создания привязок служб JavaScript для Node и попытки заставить их работать в движке React Native JavaScript. Однако давняя проблема показывает, что текущая реализация клиента Node не будет работать в React Native без серьезной настройки, поэтому мы довольно скоро отказались от этой идеи.

План

После короткого сеанса создания набросков мы пришли к выводу, что можем использовать мост React Native и использовать сгенерированные службы-заглушки Android Java RPC без множества болезненных преобразований памяти. Кроме того, мы рассмотрели решение, реализовав только один запрос / ответ. Потоковая передача ответов станет темой для другого Дня инноваций и публикации.

Наш дизайн одного запроса gRPC от React Native к конечной точке gRPC выглядит следующим образом:

Из среды JavaScriptCore NativeModule API вызывается с объектом JavaScript в качестве полезной нагрузки запроса и возвращает Promise. React Native сохраняет объект JavaScript в общей памяти и вызывает открытый собственный метод Android. Этот метод получает экземпляр ReadableMap, который имеет доступ к свойствам объекта JavaScript, и экземпляр Java Promise для асинхронного ответа.

Затем собственный метод создает объект RPC Request из данных ReadableMap и выполняет фактический запрос, используя сгенерированную заглушку службы. Это преобразует полезную нагрузку запроса в байтовый массив буфера протокола, который отправляется на сервер.

После того, как сервер ответит на запрос, обработка ответа счастливого пути будет выглядеть так:

Сгенерированная служебная заглушка предоставляется с методом обратного вызова, который преобразует ответ сервера в объект WritableMap и разрешает JavaPromise с этим объектом. Это вызывает resolve на стороне JavaScript, который получает объект JavaScript, содержащий данные ответа.

Приятным бонусом является то, что мы можем reject предоставленное обещание, если что-то пойдет не так во время преобразования, сетевого вызова и т. Д. В JavaScript мы легко можем try .. catch такое исключение.

Реализация

Мы начнем с простого, основав наше приложение на одном из классических примеров из gRPC: hello world Android-клиент и Java-сервер. Контракт общего интерфейса RPC определяется в .proto файле следующим образом:

На основе этого определения для клиента Android может быть сгенерирован класс GreeterGrpc, который обрабатывает фактическое сетевое взаимодействие, вместе с классами HelloRequest и HellyReply, которые обрабатывают (де) сериализацию запросов и ответов.

В недавно установленном проекте React Native мы добавляем плагин com.google.protobuf и зависимости в конфигурацию Gradle приложения и настраиваем его так же, как в официальном примере Android. Теперь при каждой перекомпиляции проект React Native получает недавно сгенерированные заглушки и классы сообщений.

Теперь нам нужно использовать эти сгенерированные классы. Мы создаем модуль React Native, который предоставляет метод, который делает основной вызов заглушки доступным в JavaScript:

ResponseTask (который опущен для краткости) заботится о канале gRPC и выполняет сетевой вызов в отдельном потоке. Если что-то пойдет не так в этом потоке, responsePromise отклоняется с брошенным Exception.

Теперь все, что нам нужно сделать, это получить доступ к собственному модулю из JavaScript. Чтобы сделать работу со стороны JavaScript более надежной, мы добавляем в смесь несколько потоковых типов. С помощью проверки типов мы можем гарантировать, что читаем и записываем правильные свойства в объекты запроса / ответа JavaScript. Кроме того, хорошая IDE может намекнуть, как выглядит наш API.

Вот как выглядит наша реализация:

React Native предоставляет все собственные модули через модуль NativeModules. Поскольку собственный метод sayHello принимает аргумент Java Promise, вызов NativeModules JavaScript возвращает Promise JavaScript. Мы просто оборачиваем эту функцию некоторой дополнительной информацией о типе и экспортируем ее во вложенном виде.

Чтобы использовать его, мы можем импортировать Greeter и await результат sayHello:

И это отлично работает, передавая и преобразуя сообщение из JavaScriptCore в собственный Android на примерный сервер Java и обратно!

Работа в процессе

После этого успешного обхода мы добавили сверху красивый и блестящий пользовательский интерфейс React Native (как вы уже заметили на верхнем изображении). Я начал работать над инструментом для создания модуля Java и оболочки JavaScript с потоковыми типами, потому что друзья не позволяют друзьям писать код, который можно было бы сгенерировать . Этот инструмент использует тот же .proto файл в качестве входных данных и может быть запущен во время сборки вместе с генерацией заглушек. Однако этот инструмент далеко не закончен и не готов к производству, поскольку поддержка всех функций gRPC - это слишком много для одного дня.

Заключение

В целом это был захватывающий День инноваций, и приятно видеть, что у нас есть работающее решение gRPC для React Native на Android менее чем за день.

Пример кода проекта доступен на GitHub.