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




