React native под android

Как использовать все возможности мобильной ОС в React Native

На рынке есть несколько кроссплатформенных решений: Cordova, Xamarin, React Native и другие, менее известные. Многие мобильные разработчики считают, что кроссплатформенные решения никогда не позволят делать то, что могут нативные приложения.

В статье я развенчаю этот миф и расскажу о механизме в React Native, который позволяет сделать все, на что способно нативное приложение. Этот механизм – нативные модули. Под катом – подробное описание, как создавать нативные модули для Android и iOS.

Нативные модули в кроссплатформенной разработке под мобильники помогают сделать несколько вещей:

  • Предоставить доступ к возможностям платформы, почитать из контент-провайдеров на Android или адресную книгу на iOS
  • Обернуть стороннюю библиотеку для вызова в js
  • Обернуть уже существующий код при добавлении в приложение частей на React Native
  • Реализовать части, критические к производительности(н-р шифрование)

Примерная схема приложения на React Native

В операционной системе запущено нативное приложение. В нем на низком уровне работают рантайм React Native и код нативных модулей, созданных разработчиком приложения (или автором библиотек для React Native). Выше уровнем работает React Native Bridge – промежуточное звено между нативным кодом и js. Сам js исполняется внутри JS VM, чью роль исполняет JavaScriptCore. На iOS она предоставляется системой, на Android же приложение тащит ее в виде библиотеки.

Пишем нативный модуль

Под Android

  • Зарегистрировать пакет в ReactNativeHost
  • Создать пакет
  • Создать модуль
  • Зарегистрировать модуль в пакете
  • Создать метод в модуле

Лирическое отступление 1 – Компоненты Android

Если ваш основной бэкграунд — Android, это отступление можно пропустить. Для разработчиков с основным опытом iOS или React JS нужно узнать, что приложение под Android может содержать следующие компоненты:

  • Activity
  • BroadcastReceiver
  • Service
  • ContentProvider
  • Application

В этом контексте (кхе-кхе) нас, конечно, интересует только Application. Напомню, что этот компонент и есть обьект самого приложения. Вы можете (а для React Native приложения и должны) реализовать свой класс приложения и реализовать этим классом интерфейс ReactApplication:

Нужно это, чтобы ReactNative узнал о тех нативных пакетах, которые вы хотите использовать. Для этого наш Application должен вернуть экземпляр ReactNativeHost, в котором перечислить список пакетов:

NativeLoggerPackage — тот пакет, который мы будем с вами писать. Он будет только логировать переданные в него значения, а мы сконцентрируемся на процессе создания нативного модуля вместо фактической функциональности.

Зачем нужно, чтобы Application реализовывал ReactApplication? Потому что внутри React Native есть вот такой веселый код:

Теперь реализуем NativeLoggerPackage:

Метод createViewManagers мы опустим, нам он в этой статье неважен. А важен метод createNativeModules, который должен вернуть список созданных модулей. Модули – это классы, содержащие методы, которые можно вызвать из js. Давайте создадим NativeLoggerModule:

Модуль должен наследоваться как минимум от BaseJavaModule, если вам не нужен доступ к контексту Android. Если же в нем есть нужда, необходимо использовать другой базовый класс:

В любом случае, необходимо определить метод getName(), который вернет имя, под которым ваш модуль будет доступен в js, мы увидим это чуть позже.
Теперь давайте наконец создадим метод для js. Делается это с помощью аннотации ReactMethod:

Здесь метод logTheObject становится доступен для вызова из js. Но вряд ли мы хотим просто вызывать методы без параметров, которые ничего не возвращают. Давайте разбираться с аргументами (слева java-типы, справа js):

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

Предположим, что в нативный метод мы хотим передать js-обьект. В java будет приходить ReadableMap:

Читайте также:  Андроид что такое context

В случае массива будет передаваться ReadableArray, итерация по которому не составляет проблем:

Впрочем, если вы хотите передать первым аргументом обьект, а вторым массив, то тут тоже без сюрпризов:

Как же это добро вызывать из javascript? Нет ничего проще. Первым делом нужно импортнуть NativeModules из корневой библиотеки react-native:

А затем заимпортить наш модуль(помните, мы назвали его NativeLogger ?):

Теперь можно вызывать метод:

Работает! Но постойте, хочется же знать, все ли в порядке, удалось ли записать то, что мы хотели записать. Как насчет возвращаемых значений?

А вот возвращаемых значений у функций из нативных модулей и нет. Придется выкручиваться, передавая функцию:

В нативный код будет приходить интерфейс Callback с единственным методом invoke(Object… args). Со стороны js — это просто функция:

К сожалению, в compile-time нет инструментов сверить параметры коллбэка из нативного кода и функции в js, будьте внимательны.

К счастью, можно пользоваться механизмом промисов, которые в нативном коде поддерживаются интерфейсом Promise:

Тогда вызывать этот код можно используя async/await:

На этом работа по выставлению нативного метода в js в Android завершена. Смотрим на iOS.

Создание нативного модуля в iOS

Первым делом создаем модуль NativeLogger.h :

и его реализацию NativeLogger.m:

RCT_EXPORT_MODULE — это макрос, который регистрирует наш модуль в ReactNative под именем файла, в котором обьявлен. Если это имя в js для вас не очень подходит, вы можете его поменять:

Теперь давайте реализуем методы, которые делали для Android. Для этого нам понадобятся параметры.

Для обьявления метода можно использовать макрос RCT_EXPORT_METHOD:

Самое интересное тут — поддержка промисов. Для этого придется воспользоваться другим макросом RCT_REMAP_METHOD, который первым аргументом принимает имя метода для js, а вторым и последующими — уже сигнатуру метода в objective-c.
Вместо интерфейса тут передаются два аргумента, RCTPromiseResolveBlock для резолва промиса и RCTPromiseRejectBlock для реджекта:

На этом все. Механизм передачи событий из нативных модулей в js мы рассмотрим в отдельной статье.

Нюансы

Помните, что основная идея нативных модулей — абстракция операционной системы для кроссплатформенного кода. Это значит, что интерфейс модуля должен быть согласован между Android и iOS. Автоматических средств, как это контролировать я, к сожалению, не знаю.

  • Помните, что по отдельности js и нативный код работают быстро. Бриджинг же между ними относительно медленный. Не стоит писать js циклы, в которых вызывать нативный модуль — перенесите цикл в натив.
  • Источник

    Как я написал мобильное приложение на react-native

    Меня зовут Алексей Андросов, я уже много лет работаю в Яндексе фронтенд-разработчиком. Два года назад мне и моим партнерам пришла в голову идея создать мобильную социальную сеть Verb. Идея Verb в том, что пользователи могут делиться небольшими статусами — “вербами” — о том, чем они занимаются в данную минуту, простыми вещами, о которых хочется рассказать друзьям, но некуда об этом написать. И мы уже даже получили инвестиции, но сейчас не об этом. Сегодня я хочу рассказать о том, как и почему я написал мобильное приложение на react-native.

    У меня есть бэкграунд бекенд-разработчика, более 10 лет опыта веб-разработки, но создание мобильного приложения для меня было абсолютно новой областью. Что было делать в такой ситуации? Быстро изучить Objective-C или Swift для iOS и Java для Android? Параллельно писать два приложения сразу показалось плохой идеей, я один явно не смог бы быстро реализовывать все идеи на двух платформах. Поэтому я начал искать варианты для кроссплатформенной разработки.

    Как раз кстати пришлась шумиха вокруг React, появился react-native. Тогда мне и захотелось его попробовать. Оглядываясь назад, выбор на react-native пал, по большому счету, не из-за каких-то объективных причин, а скорее из-за субъективных: до него уже были PhoneGap (WebView на JavaScript, CSS, HTML), Xamarin (C#) и NativeScript (JavaScript, TypeScript, Angular).

    Опыт работы с PhoneGap у меня уже был и использовать его не хотелось (уже очень подкупали нативные компоненты вместо их реализации на веб-технологиях), C# я не знал, писать на Angular не хотелось, так что выбор пал на RN.

    Читайте также:  Стилус карандаш для андроида

    Как и NativeScript, RN имеет биндинги нативных компонент в JS, схожую систему работы (об этом поговорим позже) и возможности.

    Первой версией, с которой мы начали, стала 0.7.1 (текущая версия — 0.46), первый мой коммит 21 июля 2015. Поддержки Android тогда не было совсем. Она появилась только в сентябре 2015 (0.11). При этом это была “первоначальная поддержка” платформы с кучей проблем и недоработок. Пройдет еще много времени до того, как поддержка iOS и Android станет более-менее равноценной в react-native.

    Забегая вперед, скажу, что Android-версию мы в какой-то момент все же решили писать на Java. Приложение уже было нужно, а RN в тот момент оказался не готов (на текущий момент такой проблемы уже нет). А вот iOS-версия Verb полностью написана на react-native.

    Что такое react-native?

    Итак, react-native – это фреймфорк для разработки на react кроссплатформенных приложений для iOS и Android. React здесь используется именно как идея, а не реализация: JSX, компонентный подход, state, props, жизненный цикл компонентов, CSS-in-JS.

    Если проводить аналогию с обычной веб-разработкой, то тут:

      нет HTML, но есть нативные компоненты в JSX ( ,

    , )

  • нет CSS. Все стили пишутся CSS-in-JS, layout строится на полифиле для flexbox (при этом он немного отличается от стандарта, например, flex-direction по-умолчанию column, а не row).
  • нет привычного DOM API. Никаких window, document и всего подобного. Из привычных API только GeoLocation API (navigator.geolocation) и XHR
  • Верстка компонент выглядит примерно вот так:

    Чтобы было проще понять, приведу аналогию с привычным HTML. — это

    CSS выглядит более привычным:

    StyleSheet объявляет и компилирует стили, они сразу напрямую передаются в native, чтобы не гонять каждый раз. PixelRatio нужен для пропорционального увеличения размера элементов в зависимости от экрана.

    Как работает RN

    Если прекрасная статья от Tadeu Zagallo про кишки RN, я вам вкратце расскажу ее суть.

    В iOS есть три треда:

    — shadow queue — очередь обработки и отрисовки layout
    — main thread — тут работают компоненты
    — JavaScript thread — тут работает JS

    Общая схема работы выглядит так: исполняется JS (по сути React), результат работы надо передать в нативный код. Для этого есть специальный мост, который с помощью JSON передает набор инструкций. RN их исполняет и отрисовывает нативные компоненты платформы. Результат работы через тот же мост может вернуться обратно в JS.

    Все общение асинхронное. При этом самым узким местом является непосредственно мост, сериализация и десериализация данных. Отсюда интересные особенности:

    • Отправлять большие пачки данных JS native — плохая идея, будет тормозить
    • Делать большие действия на JS тоже плохая идея. Это будет лочить треды и тормозить отрисовку, никаких 60fps не получится.
    • Делать частые действия (например, анимацию или контроль скролла) на JS тоже не получится, будет тормозить.

    Итого, общее правило: не надо забывать, что мы работает с нативным кодом, а не JS. Все, что можно сделать native, должно быть native.

    Write once, run everywhere?

    Не тут-то было ). На самом деле парадигма звучит так: learn once, write everywhere. Почему так получается? Все нативно, поэтому забудьте про полную кроссплатформенность. Платформы разные, поэтому и нативные компоненты разные. У них разная логика работы и механика взаимодействия. В основном это касается компонентов навигации и взаимодействия с пользователем. Поэтому в начале разработки имеет смысл сразу накидать болванку приложения с базовыми экранами и переходами. На этом разница заканчивается, все внутренние компоненты скорее всего окажутся одинаковыми. Удобство добавляет и сам RN, весь платформоспецифичный код можно разделять на файлы component.ios.js и component.android.js, тогда для каждой платформы будет собираться своя версия без всяких if внутри.

    iOS версия Verb изнутри

    Изнутри приложение написано на react-native + redux. Redux пригодился, кстати, не только, как хорошая библиотека для организации работы с состоянием приложения, но и для общения между компонентами. RN провоцирует писать pure компоненты без сайд-эффектов или использования глобальных объектов, поэтому общение компонентов через общий state является чуть ли не единственным нормальным способом.

    • React-native-deprecated-custom-components — я использую старый Navigator, т.к. еще не переехал на новый react-navigation из-за кучи багов
    • React-native-device-info — информация про UUID, версию и локаль устройства
    • React-native-fbsdk — мост в FBSDK
    • React-native-imagepicker — моя собственная обвязка над ActionSheetIOS, CameraRoll и ImagePickerIOS для загрузки фоток
    • React-native-l20n — локализация с помощью l20n
    • React-native-linear-gradient — реализация градиентов
    • React-native-pagecontrol — мой биндинг в UIPageControl
    • React-native-photo-view — простенькая библиотека, чтобы сделать полноэкранный просмотр фоток
    • React-native-svg — биндинг в svg
    • React-native-vkontakte-login — мост в VKSDK
    Читайте также:  Кэра андроид без скина

    Статистика:

    • react-native-fabric — мост в Fabric и Crashlytics
    • React-native-google-analytics-bridge — мост в GA
    • React-native-sentry — сбор крешей и отправка в собственную инсталляцию Sentry
    • React-native-bugsnag — мост в BugSnag

    При сборе крешей самое главное получить stacktrace из JS. Fabic такого не умеет, в Sentry получше, самым лучшим оказался BugSnag. Но мы все равно решили переехать в Sentry, чтобы все проблемы были в одном месте.

    Так же мы используем геолокацию и пуши. В react-native для этого есть все необходимое.

    Важная часть — производительность. Как я уже писал, узким местом в RN является мост JS native, поэтому проблема уменьшения количества перерисовок становится во весь рост. Отсюда следует, что все советы и best practice от react и redux имеют очень большое значение. React.PureComponent или shouldCompomentUpdate, меньше connect-компонентов, плоские структуры данных, иммутабельность — все дает хороший прирост производительности.

    Неочевидные советы

    Не надо затягивать обновление версий. Раньше, когда RN выпускался раз в 2 недели и почти каждая версия имела несовместимые изменения, этот совет имел очень большое значени. За 1-2 месяца фреймворк мог измениться очень сильно, поэтому обновляться сразу на пару версию было очень сложно. Сейчас ситуация обстоит лучше, релизы раз в месяц, фреймворк уже не меняется так сильно. Но я как и раньше обновляюсь сразу, как только выходит новая версия.

    Унификация react-компонент (тут понадобится помощь дизайнера) позволяет очень быстро разрабатывать новые экраны. Вообще, компонентный подход и быстрое связывание компонент — это хорошая фишка как react, так и react-native.

    boilerplate для создания новых экранов в rn + redux достаточно большой. Как и в react+redux, так и rn+redux придется написать много кода для создания нового экрана: создать компонент экрана, зарегистрировать его в Navigator, написать actions и reducer для перехода на экран. Плюс к этому стандартный boilerplate для обработки данных для нового экрана.

    Не надо все писать на JS. Приложение надо писать максимально близко к native, искать биндинги в нужные native-компоненты или писать их самим. Уверяю, это совсем не сложно.

    Надо участвовать в сообществе. В RN достаточно багов и недоработок, но нет никаких проблем их поправить. Мейнтейнеры активно участвуют в PR. Правда, зачастую приходится исправлять баги в самом native-коде, так что Java или Objective-C придется подучить. Все мои PR приняли. Например, я исправил несколько багов в работе камеры или загрузке фотографий на сервер.

    Заключение

    Писать на react-native оказалось удобно, быстро и приятно. Он правда позволяет быстро создавать прототипы приложений и доводить их до релиза. Технология молодая, поэтому есть типичный проблемы early adopters (например, периодические несовместимые изменения). Но с каждый релизом RN становится все лучше и стабильнее, Facebook прилагает к этому много усилий и делает на него ставку. К текущему моменту часть приложения Facebook уже написано react-native, а поддержка Android достигла уровня iOS.

    Я достаточно поработал с технологией и могу сказать, что написать не слишком большое iOS-приложения, аналогичное Twitter или Instagram, вполне реально. К сожалению, глубоко попробовать android так и не получилось, поэтому утверждать не могу, но, надеюсь, что в будущем можно будет попробовать конкурировать с нашей нативной iOS-версией.

    Источник

    Оцените статью