Можно вечно смотреть на огонь, воду и на то, как Microsoft пытается заставить пользователей перейти на браузер Edge. Вот, например, новый финт — страничка-руководство «Как удалить Edge», где нет ни слова о том, как удалить браузер (удалить его с Windows, кстати, можно только в ЕС), зато подробно расписаны преимущества Edge перед Chrome.
#web
#web
Подогрев сидений по подписке, это, конечно, здорово. Но как насчет рекламы на мультимедийныом экране в вашем Jeep?
Теперь в автомобилях марки после каждой остановки появляется рекламный банер и мешает во время езды. Рекламу можно закрыть, только для этого надо каждый раз тянуться рукой до сенсорного экрана.
Теперь в автомобилях марки после каждой остановки появляется рекламный банер и мешает во время езды. Рекламу можно закрыть, только для этого надо каждый раз тянуться рукой до сенсорного экрана.
Всем привет! Меня зовут Булат, и я работаю в Mercury React Native разработчиком.
Наверняка все наслышаны о фреймворке от компании Facebook (https://reactnative.dev/), на котором можно разрабатывать кроссплатформенные мобильные приложения. Не претендую на роль знатока, но постараюсь рассказать вам немного своего мнения об этой технологии, а также о роли нативных модулей в React Native. Расскажу, почему умение писать нативные модули очень важно для React Native программиста и покажу как одна непростая задача решилась с помощью самописного нативного модуля.
Наверняка все наслышаны о фреймворке от компании Facebook (https://reactnative.dev/), на котором можно разрабатывать кроссплатформенные мобильные приложения. Не претендую на роль знатока, но постараюсь рассказать вам немного своего мнения об этой технологии, а также о роли нативных модулей в React Native. Расскажу, почему умение писать нативные модули очень важно для React Native программиста и покажу как одна непростая задача решилась с помощью самописного нативного модуля.
После окончания университета я пару лет проработал в небольшой компании, где мы создавали игры на JS, используя Phaser.js и на языке Lua, используя фреймоврк Corona SDK. Опыт был интересный. Конечно, мы не создавали крутые 3d проекты, но для начинающего разработчика это был тот еще челлендж. Игры были различные от популярных в то время «Три в ряд» до пасьянсов и даже стратегий в стиле Tower Defense. Некоторые игры можно все еще найти на различных стоковых сайтах, например: https://www.gamepix.com/play/farm-dream.
Сложность реализации заключалась в невидимых блоках посередине, сроки были также очень сжатые. Помню, как было действительно страшно не успеть доделать игру в заданные сроки.
Сложность реализации заключалась в невидимых блоках посередине, сроки были также очень сжатые. Помню, как было действительно страшно не успеть доделать игру в заданные сроки.
А эту демку делал для предыдущего доклада в Мерке, чтобы продемонстрировать возможности фреймворка: https://kidasov.github.io/bomber/ — управление с помощью стрелочек бомбы ставить на пробел. На больших экранах может подлагивать, потому что каждый спрайт карты — это отдельная картинка. Но это просто демо возможностей фреймворка.
Чем были прекрасны эти 2 года? При написании любой даже самой маленькой игры в голове генерировалось просто куча идей, как сделать игровой процесс лучше, какой алгоритм использовать. И как только появлялся хоть какой-то геймплей, то все свободное время я занимался лишь изучением возможностей фреймворка, общался на форумах о последних апдейтах. Сидишь вечерами и пытаешься экспериментировать над геймплеем и это очень увлекает, так как процесс полностью интерактивный. Помимо этого увидел реальное применение алгоритмам, о которых до только читал в книжках до этого. Много работал с массивами, графами, было очень интересно описывать игровые сущности классами, экспериментировал с паттернами программирования. И самое главное, что каждая новая игра заставляла поразмыслить над геймплеем и особенностями реализации.
Проработав в геймдеве 2 года, я попал в Mercury, где моим первым проектом стало приложение для ТВ приставки Roku, где программы пишутся на языке brightscript (который совсем не bright, писать на нем было не очень привычно). Помню, что было очень неудобно дебажить, так как у приставки было мало видеопамяти и она периодически подвисала, не выдерживая моего неправильного кода.
За годы, проведенные в Mercury, также посчастливилось поработать на Ember.js, расширяя функционал админки на одном из проектов. Было сложным привести legacy-код на проекте в порядок из-за огромного количества ненужных библиотек, во многих местах наряду с ember-компонентами использовался JQuery. Со временем вошел во вкус, и даже появилась идея все переписать на новом Ember.js, но увы данную часть проекта свернули и мне так и не удалось использовать последний Ember. К слову, фреймворк довольно неплохой. Также у Ember неплохое community и отличная документация, а также есть отличная CLI, через которую можно установить большое количество ember библиотек (аддонов).
Emberjs
Ember.js - A framework for ambitious web developers
Ember.js helps developers be more productive out of the box. Designed with developer ergonomics in mind, its friendly APIs help you get your job done—fast.
Параллельно я изучал React и React-Native. В первое время фронтовых проектов было почти столько же, сколько и мобильных, но со временем число фронтенд-проектов стало меньше и большая часть проектов, на которых я работал, была разработана на React Native. Первые впечатления от фреймворка были довольно положительные. Кроме работы с Xcode, такое большое количество настроек, каждая из которых может все поломать пугала, а обновления Xcode без немалой доли терпения точно не пережить. Но после того как настроил и привык к окружению Xcode для iOS, Android Studio для Android начала вникать в разработку кроссплатформенных приложений, используя JavaScript или TypeScript. На проектах постепенно втягивался в разработку на React Native, используя доступные библиотеки.
Однако сложность задач росла и со временем все чаще наблюдал баги, где задачи не решались стандартным способом, приходилось лезть под капот React Native модуля и разбираться. Спасибо отзывчивым коллегам и форумам, которые помогали понять, что в нативном коде есть ошибка и ее нужно поправить. Часами напролет я пытался найти решение, чтобы решить задачу. Но лезть в нативный код мне также не хотелось. Поначалу я хотел делегировать задачи, связанные с нативом, однако потом мне это надоело и я решил потихонечку разбираться и пробовать свои силы в редактировании нативного кода и исправлении существующих библиотек под свои нужды. Поначалу приходилось форкать библиотеку, но с появлением patch-package написание патчей стало гораздо проще.
Однако сложность задач росла и со временем все чаще наблюдал баги, где задачи не решались стандартным способом, приходилось лезть под капот React Native модуля и разбираться. Спасибо отзывчивым коллегам и форумам, которые помогали понять, что в нативном коде есть ошибка и ее нужно поправить. Часами напролет я пытался найти решение, чтобы решить задачу. Но лезть в нативный код мне также не хотелось. Поначалу я хотел делегировать задачи, связанные с нативом, однако потом мне это надоело и я решил потихонечку разбираться и пробовать свои силы в редактировании нативного кода и исправлении существующих библиотек под свои нужды. Поначалу приходилось форкать библиотеку, но с появлением patch-package написание патчей стало гораздо проще.
GitHub
GitHub - ds300/patch-package: Fix broken node modules instantly 🏃🏽♀️💨
Fix broken node modules instantly 🏃🏽♀️💨. Contribute to ds300/patch-package development by creating an account on GitHub.
Наступил момент, когда доступных решений на некоторых проектах просто не было, кроме как использовать нативные SDK. Например, на одном из проектов нам нужно было интегрировать нативный компонент для iOS, который мы не могли найти в React Native библиотеке, на другом пришлось писать модуль для работы с платежной системой PayPall. Медленно, но верно я начал вникать и пытаться что-то сделать в нативном коде, хоть это было и довольно сложно для человека незнакомого с Objective-C.
На последнем из проектов появились задачи посложнее, например, коннектиться к стороннему девайсу — панорамной камере ricoh theta, — и показывать с нее лайв превью, фотографировать и многое другое. Пришлось писать нативный модуль с нуля для iOS и Android для отображения этого самого лайв превью. Поначалу это кажется непосильной задачей, но со временем появляется какое-то понимание. Тут мне хочется немного остановиться и рассказать про ценность и необходимость написания нативных модулей для React Native разработчика. об этом уже завтра. А пока, если есть какие-то идеи или вопросы, приглашаю пообщаться в комментариях 👇
На последнем из проектов появились задачи посложнее, например, коннектиться к стороннему девайсу — панорамной камере ricoh theta, — и показывать с нее лайв превью, фотографировать и многое другое. Пришлось писать нативный модуль с нуля для iOS и Android для отображения этого самого лайв превью. Поначалу это кажется непосильной задачей, но со временем появляется какое-то понимание. Тут мне хочется немного остановиться и рассказать про ценность и необходимость написания нативных модулей для React Native разработчика. об этом уже завтра. А пока, если есть какие-то идеи или вопросы, приглашаю пообщаться в комментариях 👇
Ricoh360
THETA | RICOH360
Anyone can easily capture and share images and video with the 360-degree camera and app
Всем привет! Сегодня расскажу, что же такое нативные модули и как они работают. Все приложения на React Native мы пишем, используя React, JavaScript или TypeScript. Но для определенных задач, например, для общения со сторонними девайсами для которых доступен только нативный SDK, нам придется писать нативный код. Пишем мы нативные модули на языке Objective-C или Swift для iOS, Java или Kotlin для Android. Другими словами, мы можем написать нативный модуль для доступа к нужному нам API и использовать его в нашем React Native проекте
Старая архитектура vs новая архитектура
Когда React Native только появился, то команда Facebook предложила следующую концепцию. JavaScript-инструкция отправлялась через bridge выполняя код из нативного модуля, затем результат выполнения отправляется обратно в JavaScript-код через bridge. Эта операция является асинхронной, обозначая, что JavaScript-код может выполняться не дожидаясь возвращения результата от нативного модуля. Данный подход нужен для того, чтобы приложение осталось отзывчивым, особенно для задач, которые занимают много времени, например, обработка изображений или доступ к камере, к Bluetooth, NFC, работа с OpenGL.
Проблемы старой архитектуры
1. Большое количество событий, требующих сериализации / десериализации (передающихся через Bridge) становится бутылочным горлышком и замедляет UI.
2. Асинхронность: коммуникация через bridge всегда асинхронна, что делает невозможным выполнение синхронных вызовов между JavaScript и нативными модулями.
Разберем на примере игры с обновлениями в реальном времени, которую мы захотим написать на React Native, где пользовательские вводы отправляются в нативный код для обработки — например, для определения столкновений в физическом движке. Мост (bridge) создает задержки из-за времени, необходимого на:
• Сериализацию входных данных в формат JSON;
• Отправку данных в нативный модуль;
• Обработку данных на нативной стороне;
• Сериализацию и возврат ответа обратно в JavaScript.
Эта задержка делает приложение менее отзывчивым и может вызывать зависания и лаги в приложениях, работающих в реальном времени. Пользователи могу столкнуться с заметными задержками.
Когда React Native только появился, то команда Facebook предложила следующую концепцию. JavaScript-инструкция отправлялась через bridge выполняя код из нативного модуля, затем результат выполнения отправляется обратно в JavaScript-код через bridge. Эта операция является асинхронной, обозначая, что JavaScript-код может выполняться не дожидаясь возвращения результата от нативного модуля. Данный подход нужен для того, чтобы приложение осталось отзывчивым, особенно для задач, которые занимают много времени, например, обработка изображений или доступ к камере, к Bluetooth, NFC, работа с OpenGL.
Проблемы старой архитектуры
1. Большое количество событий, требующих сериализации / десериализации (передающихся через Bridge) становится бутылочным горлышком и замедляет UI.
2. Асинхронность: коммуникация через bridge всегда асинхронна, что делает невозможным выполнение синхронных вызовов между JavaScript и нативными модулями.
Разберем на примере игры с обновлениями в реальном времени, которую мы захотим написать на React Native, где пользовательские вводы отправляются в нативный код для обработки — например, для определения столкновений в физическом движке. Мост (bridge) создает задержки из-за времени, необходимого на:
• Сериализацию входных данных в формат JSON;
• Отправку данных в нативный модуль;
• Обработку данных на нативной стороне;
• Сериализацию и возврат ответа обратно в JavaScript.
Эта задержка делает приложение менее отзывчивым и может вызывать зависания и лаги в приложениях, работающих в реальном времени. Пользователи могу столкнуться с заметными задержками.
В новой архитектуре нативные модули стали называться TurboModules. Главным нововведением стало удаление bridge и замена его на JSI (JavaScript Interface). В старой архитектуре много времени уходило на отправку и получение больших сериализованных сообщений через bridge, а TurboModules позволяют JavaScript напрямую дергать нативный код без использования бриджа.
Основные изменения:
1. Удаление bridge и добавление JSI. JSI предоставляет JavaScript прямой доступ к нативным объектам, что позволяет выполнять операции быстрее и с меньшими накладными расходами.
2. Lazy Click Me Load More, в старой архитектуре все нативные модули подгружались при загрузке приложения, а в новой только по необходимости. Соответственно есть выигрыш в памяти.
3. Бридж сериализовывал сообщения и десериализовывал, что может также замедлять работу приложения.
4. TurboModules получают и отправляют данные напрямую через JSI.
5. В TurboModules улучшено управление потоками.
6. Fabric (новый UI рендеринг) — обеспечивает более быстрый рендеринг пользовательского интерфейса. Поддерживает асинхронное обновление UI, что предотвращает блокировки основного потока. Позволяет обновлять только измененные части UI вместо перерисовки всего экрана.
Как итог TurboModules смогли улучшить производительность, оптимизировать использование ресурсов, и улучшили работу анимаций, время загрузки и отзывчивость приложения.
Основные изменения:
1. Удаление bridge и добавление JSI. JSI предоставляет JavaScript прямой доступ к нативным объектам, что позволяет выполнять операции быстрее и с меньшими накладными расходами.
2. Lazy Click Me Load More, в старой архитектуре все нативные модули подгружались при загрузке приложения, а в новой только по необходимости. Соответственно есть выигрыш в памяти.
3. Бридж сериализовывал сообщения и десериализовывал, что может также замедлять работу приложения.
4. TurboModules получают и отправляют данные напрямую через JSI.
5. В TurboModules улучшено управление потоками.
6. Fabric (новый UI рендеринг) — обеспечивает более быстрый рендеринг пользовательского интерфейса. Поддерживает асинхронное обновление UI, что предотвращает блокировки основного потока. Позволяет обновлять только измененные части UI вместо перерисовки всего экрана.
Как итог TurboModules смогли улучшить производительность, оптимизировать использование ресурсов, и улучшили работу анимаций, время загрузки и отзывчивость приложения.
Далее хочу рассказать об одном нативном модуле, который я написал, чтобы решить довольно непростую проблему в проекте.
У нас есть проект для риэлторов, в котором необходимо подключаться через приложение к 360 камере и делать панорамные снимки. На камере есть специальный баркод, такой маленький в виде data matrix, в котором зашит ssid нашей камеры. Нам необходимо было отсканировать его, считать ssid и подключиться к камере через Wi-Fi. Сперва мы искали существующие решения, но библиотеки просто не могли отсканировать такой маленький баркод, а задать область было невозможно. Перебрали немало библиотек, какие-то из них могли распарсить маленький баркод на iOS, например, библиотека react-native-vision-camera позволяла это сделать, но на Android не получалось ничего отсканировать, так как баркод был и инвертированный к тому же. Поэтому сперва его нужно было инвертировать и только потом отсканировать. Было принято решение написать кастомный нативный модуль, про который я сейчас немного расскажу. На прикрепленной картинке изображена нижняя часть камеры с кодом YN14010033, который вшит в дата матрицу справа от этого кода.
У нас есть проект для риэлторов, в котором необходимо подключаться через приложение к 360 камере и делать панорамные снимки. На камере есть специальный баркод, такой маленький в виде data matrix, в котором зашит ssid нашей камеры. Нам необходимо было отсканировать его, считать ssid и подключиться к камере через Wi-Fi. Сперва мы искали существующие решения, но библиотеки просто не могли отсканировать такой маленький баркод, а задать область было невозможно. Перебрали немало библиотек, какие-то из них могли распарсить маленький баркод на iOS, например, библиотека react-native-vision-camera позволяла это сделать, но на Android не получалось ничего отсканировать, так как баркод был и инвертированный к тому же. Поэтому сперва его нужно было инвертировать и только потом отсканировать. Было принято решение написать кастомный нативный модуль, про который я сейчас немного расскажу. На прикрепленной картинке изображена нижняя часть камеры с кодом YN14010033, который вшит в дата матрицу справа от этого кода.
Проблемы готовых решений
Отсутствие одинакового поведения на нескольких платформах.Так на iOS могло работать на ультра широкой камере, на Android не хотелось заводиться совсем.
1. Частые ошибки при сканировании из-за недостаточного освещения.
2. Отсутствие сканирования по области.
3. Нам необходимо было сделать процесс сканирования удобным, дабы избежать ситуации, когда пользователь пытается безуспешно несколько минут отсканировать код, но с готовыми библиотеками об этом сразу можно было забыть.
Отсутствие одинакового поведения на нескольких платформах.Так на iOS могло работать на ультра широкой камере, на Android не хотелось заводиться совсем.
1. Частые ошибки при сканировании из-за недостаточного освещения.
2. Отсутствие сканирования по области.
3. Нам необходимо было сделать процесс сканирования удобным, дабы избежать ситуации, когда пользователь пытается безуспешно несколько минут отсканировать код, но с готовыми библиотеками об этом сразу можно было забыть.
Проблема многих библиотек под React Native в том, что они работают для общих случаев, тот же QR код платежный сканировался практически везде. Но если случай не самый обычный, то частенько не остается никаких альтернатив, кроме как взяться за написание нативного модуля, чем я в итоге и занялся, бросив попытки найти ту самую библиотеку. Самое интересное, что мозг пытается верить, что библиотечку удастся настроить под свои нужды, и довольно сложно заставить себя открывать тот же XCode и Android Studio и пытаться по мануалам написать свой первый нативный модуль, который будет работать так как нужно тебе. И здесь хочу отметить, что в этом месте я был немного разочарован в React Native, что все также возникает необходимость писать нативный код, иногда даже много нативного кода. Хотя фреймворк обещает Learn Once. Write Everywhere, мне довольно часто приходилось лезть внутрь библиотек и разбираться, почему ничего не работает и патчить на свой страх и риск. Поэтому, на мой взгляд, разрабатывать для React Native без знания натива достаточно сложно. Для простых экранов и относительно стандартного UI можно найти много библиотек, которые просто будут работать из коробки, но если требуется что-то кастомное, то же подключение к сторонним устройствам, сканирование баркодов и многое другое может отнять кучу нервов и заставить погрузиться в мир нативной разработки хоть чуть-чуть.
После того как мы обозначили важность нативных модулей в коде, предлагаю немного погрузиться в имплементацию данного модуля. Напомню, что основная цель это сканирование маленького баркода камеры для автоматического подключения к камере по Wi-Fi. Данный модуль состоит из трех частей.
1.
2.
3.
Приложу ссылку на исходный код, если хотите взглянуть https://github.com/kidasov/qrparser
1.
BarcodeView
— это обычная вьюха, которая показывает превью с камеры и по сути является нашим React-компонентом. Данная вьюха показывает поток с камеры, позволяя пользователю видеть картинку в реальном времени.2.
BarcodeCameraManager
— это класс, который настраивает камеру и превью с ней. Для настрйоки камеры требуется создать camera input, camera output и настроить capture session. По факту данный класс полностью несет ответственность за работу с камерой, позволяя выбрать нужную камеру, настроить фокус. А также данный класс принимает колбэки, которые перенаправляют видеопоток в функцию, которая сканирует баркод.3.
BarcodeScanner
— класс, где случается вся магия. Класс, который проверяет каждый видеофрейм на наличие баркода, накладывая фильтры на изображение для улучшения видимости этого баркода (накладываем grayscale для улучшения контраста и инвертируем картинку, чтобы алгоритмы Vision Framework смогли распознать код).Приложу ссылку на исходный код, если хотите взглянуть https://github.com/kidasov/qrparser
Далее расскажу вкратце об исходном коде.
При добавлении в родительскую вьюху запускается
BarcodeView
— это UIView, аналог View в React Native, в котором мы инициализируем инстансы BarcodeCameraManager
и BarcodeDetector
, в качестве параметров данный компонент принимает функцию onBarcodeRead
, которая вызывается в момент успешного сканирования баркода.При добавлении в родительскую вьюху запускается
setupCameraLiveView()
, чтобы включить камеру. При удалении из родительской вьюхи вызывается stopCameraLiveView()
для освобождения ресурсов. Также компонент реализует протокол BackgroundListenerDelegate
, при переходе в background срабатывает функция onBackgroundMove
, которая останавливает работу камеры при переходе приложения в фон. При переходе в foreground срабатывает onForegroundMove
, которая перезапускает камеру при возвращении приложения в foreground. Также, для дебага мы отображаем картинку, в которой мы ищем баркод и отрисовываем ее в функции _DebugSetPhotoImagePreview
. Код можно посмотреть здесь https://github.com/kidasov/qrparser/blob/main/ios/BarcodeView.swiftСледующим у нас идет код класса
В функции
https://github.com/kidasov/qrparser/blob/main/ios/BarcodeCameraManager.swift
BarcodeCameraManager
, который выполняет настройку камеры, захват видео и обработку кадров. Перечислим свойства данного класса.backCamera
— устройство захвата видеопотока (задняя камера в нашем случае). captureVideoOutput
— выходной поток для получения видеокадров.captureSession
— сессия захвата, которая связывает входы (камера) и выходы (видео). cameraPreviewLayer
— слой для отображения видео с камеры в пользовательском интерфейсе.В функции
setupCamera
мы настраиваем камеру, устанавливаем разрешение для видеопотока hd1920x1080. Затем в configureBackCamera
мы находим заднюю камеру телефона, далее настраиваем input и output для нашей сессии, и в функции startCaptureSession запускаем сессию. Класс реализует протокол AVCaptureVideoDataOutputSampleBufferDelegate
, что позволяет получать каждый кадр из видеопотока: метод captureOutput(_:didOutput:from:):
вызывается при получении нового кадра и передает кадр в onCaptureOutput
для дальнейшей обработки (для сканирования баркода).https://github.com/kidasov/qrparser/blob/main/ios/BarcodeCameraManager.swift
Расскажу еще об одном челлендже при попытке отсканировать наш баркод. Многие библиотеки сканировали баркод только с помощью широкогоугольной камеры (
builtInUltraWideCamera
), но этой камеры у нас не было на iPhone X, поэтому это был еще один аргумент в пользу написания кастомного модуля для того, чтобы баркод сканировался и на устаревших моделях iPhone. В коде мы используем builtInWideAngleCamera
— это системное обозначение основной камеры на устройствах iOS. Эта камера является стандартной для всех iPhone, начиная с самых ранних моделей.