You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native开发:如何在商米V2安卓POS设备上以编程方式启用/禁用NFC

React Native开发:如何在商米V2安卓POS设备上以编程方式启用/禁用NFC

嘿,我之前帮朋友处理过商米V2 POS设备的React Native开发需求,刚好碰到过NFC自动开关的问题,给你捋捋可行的方案:

首先得明确一点:普通安卓第三方APP默认是没有权限直接控制NFC开关的,因为系统把这个权限限制得很严,防止恶意APP乱改硬件设置。但商米V2是定制化的POS设备,厂商针对行业应用开放了一些特殊接口,这才让自动控制NFC成为可能。

你当前用的react-native-nfc-manager的局限

这个库主要是提供NFC读写的能力,以及跳转到系统NFC设置页的快捷方式,但它并没有封装直接控制NFC开关的原生接口——毕竟这涉及到系统级权限,不是通用RN库能覆盖的。

可行的解决方案:基于商米设备SDK+原生桥接

商米官方给自家设备提供了专属的硬件控制能力,我们需要通过React Native的原生模块桥接来调用这些能力,具体步骤如下:

1. 配置安卓项目权限

首先在你的安卓AndroidManifest.xml里添加必要的权限:

<!-- NFC基础权限 -->
<uses-permission android:name="android.permission.NFC" />
<!-- 修改系统设置的权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- 商米设备硬件控制专属权限 -->
<uses-permission android:name="com.sunmi.permission.HARDWARE_CONTROL" />

注意:WRITE_SETTINGS权限光在Manifest里加还不够,需要在APP运行时主动向用户申请授权,你可以用RN的权限处理工具或者原生代码来完成这一步。

2. 编写安卓原生控制NFC的代码

商米V2基于安卓系统,我们可以直接调用安卓原生的NfcAdapter类方法,但要注意:enable()disable()方法需要系统级调用权限,商米设备对注册过的行业应用会开放这个权限(如果是正式商用APP,记得在商米开发者平台完成应用注册和权限申请)。

先写一个自定义的React Native模块,比如SunmiNfcModule.java

package com.yourappname;

import android.nfc.NfcAdapter;
import android.content.Context;
import android.provider.Settings;
import android.Manifest;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class SunmiNfcModule extends ReactContextBaseJavaModule {
    private NfcAdapter nfcAdapter;

    public SunmiNfcModule(ReactApplicationContext reactContext) {
        super(reactContext);
        nfcAdapter = NfcAdapter.getDefaultAdapter(reactContext);
    }

    @Override
    public String getName() {
        return "SunmiNfcModule";
    }

    // 启用NFC的方法
    @ReactMethod
    public void enableNfc(Promise promise) {
        if (nfcAdapter == null) {
            promise.reject("NFC_NOT_SUPPORTED", "设备不支持NFC");
            return;
        }

        // 检查是否有修改系统设置的权限
        if (!Settings.System.canWrite(getReactApplicationContext())) {
            promise.reject("PERMISSION_DENIED", "缺少修改系统设置的权限");
            return;
        }

        try {
            if (ActivityCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.NFC) != PackageManager.PERMISSION_GRANTED) {
                promise.reject("NFC_PERMISSION_DENIED", "缺少NFC基础权限");
                return;
            }
            nfcAdapter.enable();
            promise.resolve(true);
        } catch (SecurityException e) {
            promise.reject("SECURITY_ERROR", "无权限控制NFC开关,请检查商米设备权限配置");
        }
    }

    // 禁用NFC的方法
    @ReactMethod
    public void disableNfc(Promise promise) {
        if (nfcAdapter == null) {
            promise.reject("NFC_NOT_SUPPORTED", "设备不支持NFC");
            return;
        }

        if (!Settings.System.canWrite(getReactApplicationContext())) {
            promise.reject("PERMISSION_DENIED", "缺少修改系统设置的权限");
            return;
        }

        try {
            if (ActivityCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.NFC) != PackageManager.PERMISSION_GRANTED) {
                promise.reject("NFC_PERMISSION_DENIED", "缺少NFC基础权限");
                return;
            }
            nfcAdapter.disable();
            promise.resolve(true);
        } catch (SecurityException e) {
            promise.reject("SECURITY_ERROR", "无权限控制NFC开关,请检查商米设备权限配置");
        }
    }

    // 检查NFC当前状态
    @ReactMethod
    public void isNfcEnabled(Promise promise) {
        if (nfcAdapter == null) {
            promise.resolve(false);
            return;
        }
        promise.resolve(nfcAdapter.isEnabled());
    }
}

然后注册这个模块,创建SunmiNfcPackage.java

package com.yourappname;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SunmiNfcPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new SunmiNfcModule(reactContext));
        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

最后在MainApplication.java里添加这个Package:

// 导入刚才的Package
import com.yourappname.SunmiNfcPackage;

// 在getPackages()方法里添加自定义模块
@Override
protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new SunmiNfcPackage());
    return packages;
}

3. 在React Native JS层调用原生方法

现在你可以在RN组件里直接调用封装好的方法了,比如:

import { NativeModules, PermissionsAndroid } from 'react-native';
import { useState, useEffect } from 'react';

const { SunmiNfcModule } = NativeModules;

const NfcScanComponent = () => {
    const [isScanning, setIsScanning] = useState(false);
    const [nfcEnabled, setNfcEnabled] = useState(false);

    // 初始化时检查NFC状态
    useEffect(() => {
        checkNfcStatus();
    }, []);

    // 申请修改系统设置的权限
    const requestWriteSettingsPermission = async () => {
        try {
            const granted = await PermissionsAndroid.request(
                PermissionsAndroid.PERMISSIONS.WRITE_SETTINGS,
                {
                    title: "需要修改系统设置权限",
                    message: "应用需要权限来自动控制NFC开关",
                    buttonNeutral: "稍后再说",
                    buttonNegative: "取消",
                    buttonPositive: "允许"
                }
            );
            return granted === PermissionsAndroid.RESULTS.GRANTED;
        } catch (err) {
            console.warn(err);
            return false;
        }
    };

    // 检查NFC当前状态
    const checkNfcStatus = async () => {
        try {
            const enabled = await SunmiNfcModule.isNfcEnabled();
            setNfcEnabled(enabled);
        } catch (error) {
            console.error('检查NFC状态失败:', error);
        }
    };

    // 切换NFC开关
    const toggleNfc = async () => {
        const hasPermission = await requestWriteSettingsPermission();
        if (!hasPermission) {
            alert('请授予修改系统设置的权限');
            return;
        }

        try {
            if (nfcEnabled) {
                await SunmiNfcModule.disableNfc();
                alert('NFC已关闭');
            } else {
                await SunmiNfcModule.enableNfc();
                alert('NFC已开启');
            }
            setNfcEnabled(!nfcEnabled);
        } catch (error) {
            console.error('控制NFC失败:', error);
            alert(`操作失败: ${error.message}`);
        }
    };

    return (
        <View style={{ padding: 20 }}>
            {/* 你的NFC扫描UI可以放在这里 */}
            <Button 
                title={nfcEnabled ? '关闭NFC' : '开启NFC'} 
                onPress={toggleNfc}
                style={{ marginTop: 20 }}
            />
        </View>
    );
};

export default NfcScanComponent;

关键注意事项

  • 商米权限配置:如果是正式商用APP,一定要在商米开发者平台完成应用注册和硬件控制权限申请,不然调试时可能会碰到权限拒绝的错误。
  • 调试技巧:本地调试时,可以通过商米的调试工具或者adb命令把APP设为系统应用,这样能绕过部分权限限制快速验证功能。
  • 兼容性:这个方案只适用于商米V2这类定制POS设备,普通安卓手机就算获取了权限也可能被系统拦截,毕竟商米是针对行业场景开放了特殊权限。

内容来源于stack exchange

火山引擎 最新活动