Розробка 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
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Розробка Native Module для React Native-додатків (Android)

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

Стара архітектура: Bridge Module

Для проектів на 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 падає з десятків мс до одиниць.

Надіслання подій з нативного коду в JavaScript

Паттерн однаковий для обох архітектур—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 є обов'язковим. Витоки підписок призводять до виклику обробника після розмонтування компонента й потенційного оновлення стану розмонтованого компонента.

Обробка дозволів всередину модуля

Нативний модуль не повинен самостійно запитувати дозволи під час виконання—це відповідальність шару 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(). Перевірте журналів bundler Metro та adb logcat.

Виклик нативного методу з фонового потоку оновлює UI. React Native bridge та JSI — це не головний потік. Якщо ви оновлюєте якийсь Android UI-елемент всередину @ReactMethod, використовуйте 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 вендора розраховується індивідуально. Вартість визначається після аналізу вимог.