Разработка Native Module для React Native-приложения (Android)

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Разработка Native Module для React Native-приложения (Android)
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Разработка Native Module для React Native-приложения (Android)

Native Module нужен тогда, когда JavaScript не может дотянуться до нужного Android API: Bluetooth LE, NFC, специфичный SDK от вендора оборудования, работа с файловой системой ниже уровня что даёт react-native-fs. В 2024 году React Native предлагает два пути: старая архитектура (Bridge) и новая (JSI + TurboModules). Проект, созданный с react-native init версии 0.74+, по умолчанию использует новую архитектуру.

Старая архитектура: Bridge-модуль

Для проектов на RN < 0.68 или с отключённой новой архитектурой:

// BluetoothModule.kt
class BluetoothModule(private val reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {

    override fun getName(): String = "BluetoothModule"

    @ReactMethod
    fun isBluetoothEnabled(promise: Promise) {
        val bluetoothManager = reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        promise.resolve(bluetoothManager.adapter?.isEnabled ?: false)
    }

    @ReactMethod
    fun startScan(promise: Promise) {
        val scanner = (reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager)
            .adapter?.bluetoothLeScanner
        if (scanner == null) {
            promise.reject("BT_ERROR", "Bluetooth LE not supported")
            return
        }
        // запуск сканирования
        promise.resolve(null)
    }

    // Отправка событий в JS
    private fun sendEvent(eventName: String, params: WritableMap?) {
        reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            .emit(eventName, params)
    }
}

Регистрация через Package:

class BluetoothPackage : ReactPackage {
    override fun createNativeModules(context: ReactApplicationContext) =
        listOf(BluetoothModule(context))
    override fun createViewManagers(context: ReactApplicationContext) = emptyList<ViewManager<*, *>>()
}

Добавить в MainApplication.kt в метод getPackages().

Новая архитектура: TurboModule + JSI

С RN 0.74+ и включённым newArchEnabled=true модуль реализует TurboModule через Codegen. Сначала — спецификация на TypeScript:

// NativeBluetoothModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  isBluetoothEnabled(): Promise<boolean>;
  startScan(): Promise<void>;
  addListener(eventType: string): void;
  removeListeners(count: number): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('BluetoothModule');

Codegen генерирует NativeBluetoothModuleSpec.kt из этой спецификации. Нативная реализация наследует сгенерированный абстрактный класс:

class BluetoothModule(context: ReactApplicationContext) :
    NativeBluetoothModuleSpec(context) {

    override fun getName() = NAME

    override fun isBluetoothEnabled(): Promise<Boolean> {
        // реализация идентична bridge-варианту
    }

    companion object {
        const val NAME = "BluetoothModule"
    }
}

Главное отличие: TurboModule вызывается через JSI напрямую, без сериализации через JSON-мост. Для высокочастотных вызовов (аудиопоток, датчики) это критично — latency падает с десятков мс до единиц.

Передача событий из нативного кода в JS

Паттерн один для обеих архитектур — RCTDeviceEventEmitter:

fun emitScanResult(deviceAddress: String, rssi: Int) {
    val params = Arguments.createMap().apply {
        putString("address", deviceAddress)
        putInt("rssi", rssi)
    }
    reactApplicationContext
        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
        .emit("onBluetoothDeviceFound", params)
}

На JS стороне:

import { NativeEventEmitter, NativeModules } from 'react-native';

const { BluetoothModule } = NativeModules;
const emitter = new NativeEventEmitter(BluetoothModule);

useEffect(() => {
  const subscription = emitter.addListener('onBluetoothDeviceFound', (event) => {
    console.log('Found device:', event.address, 'RSSI:', event.rssi);
  });
  return () => subscription.remove();
}, []);

subscription.remove() в cleanup useEffect — обязательно. Утечка подписки приводит к вызову обработчика после размонтирования компонента и потенциальному обновлению размонтированного state.

Работа с разрешениями внутри модуля

Нативный модуль не должен самостоятельно запрашивать рантайм-разрешения — это ответственность JS-слоя через react-native-permissions или встроенный PermissionsAndroid. Модуль только проверяет наличие разрешения и возвращает соответствующую ошибку:

@ReactMethod
fun startScan(promise: Promise) {
    if (ActivityCompat.checkSelfPermission(
            reactContext,
            Manifest.permission.BLUETOOTH_SCAN
        ) != PackageManager.PERMISSION_GRANTED) {
        promise.reject("PERMISSION_DENIED", "BLUETOOTH_SCAN permission required")
        return
    }
    // продолжение
}

Отладка и типичные ошибки

Модуль не найден: null is not an object (evaluating 'NativeModules.BluetoothModule.isBluetoothEnabled') — Package не добавлен в getPackages() или опечатка в getName(). Проверить через Metro bundler логи и adb logcat.

Вызов нативного метода из фонового потока обновляет UI. React Native bridge и JSI — не main thread. Если внутри @ReactMethod обновляете какой-то Android UI-элемент — нужен Handler(Looper.getMainLooper()).post { ... }.

WritableMap после передачи в promise уже нельзя использовать. Arguments.createMap() создаёт одноразовый объект. После promise.resolve(map) этот объект уже не валиден — попытка прочитать его снова даёт IllegalStateException.

Совместимость старой и новой архитектуры. До полного перехода проекта на новую архитектуру модуль должен поддерживать обе. ReactPackage остаётся, TurboModule-реализация добавляется поверх. В build.gradle — условная компиляция через isNewArchEnabled.

Тестирование

Нативный модуль тестируется на уровне Kotlin (JUnit + Mockk для ReactApplicationContext) и на уровне интеграции через Detox или Maestro. Изолированный тест bridge-метода:

@Test
fun `isBluetoothEnabled returns false when adapter is null`() {
    val context = mockk<ReactApplicationContext>()
    every { context.getSystemService(Context.BLUETOOTH_SERVICE) } returns mockk<BluetoothManager> {
        every { adapter } returns null
    }
    val module = BluetoothModule(context)
    val promise = mockk<Promise>(relaxed = true)
    module.isBluetoothEnabled(promise)
    verify { promise.resolve(false) }
}

Разработка Native Module: базовый модуль с 3-5 методами — 2-4 дня. Модуль с событиями, поддержкой обеих архитектур и тестами — от недели. Интеграция тяжёлого вендорского SDK — индивидуально. Стоимость рассчитывается после анализа требований.