React Native外接USB设备读取指定文件的Java模块扩展需求
React Native USB设备文件查找与分享功能扩展方案
针对你的需求,我们在已有的USB设备监听模块基础上,添加USB权限请求、存储设备识别、文件查找和系统分享功能,以下是完整实现方案:
一、扩展后的Java原生模块代码
package com.awesomeproject.usb; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.net.Uri; import android.os.Environment; import android.util.Log; import android.app.PendingIntent; import androidx.annotation.NonNull; import androidx.core.content.FileProvider; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import java.io.File; import java.util.HashMap; import java.util.Locale; public class UsbDeviceListenerModule extends ReactContextBaseJavaModule implements LifecycleEventListener { private static final String TAG = "UsbDeviceListener"; private static final String ACTION_USB_PERMISSION = "com.awesomeproject.USB_PERMISSION"; private static final String TARGET_FILE_NAME = "DataXYZ.txt"; private final ReactApplicationContext reactContext; private final BroadcastReceiver usbReceiver; private final BroadcastReceiver permissionReceiver; private boolean isInitialized = false; public UsbDeviceListenerModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; reactContext.addLifecycleEventListener(this); // USB设备连接/断开监听 usbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { // 仅处理存储类设备 if (device.getDeviceClass() == UsbDevice.USB_CLASS_MASS_STORAGE) { requestUsbPermission(device); } else { sendDeviceConnectedEvent(device, false); } } } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { sendDeviceDisconnectedEvent(device); } } } }; // USB权限请求结果监听 permissionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (device != null) { sendDeviceConnectedEvent(device, true); // 权限获取成功,开始查找文件 findAndShareTargetFile(); } } else { Log.d(TAG, "USB权限被拒绝"); sendDeviceConnectedEvent(device, false); } } } } }; // 注册广播接收器 IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); reactContext.registerReceiver(usbReceiver, filter); IntentFilter permissionFilter = new IntentFilter(ACTION_USB_PERMISSION); reactContext.registerReceiver(permissionReceiver, permissionFilter); } @NonNull @Override public String getName() { return "UsbDeviceListener"; } @Override public void invalidate() { super.invalidate(); reactContext.unregisterReceiver(usbReceiver); reactContext.unregisterReceiver(permissionReceiver); reactContext.removeLifecycleEventListener(this); } @Override public void onHostResume() { if (!isInitialized) { checkForConnectedUsbDevices(); isInitialized = true; } } @Override public void onHostPause() {} @Override public void onHostDestroy() {} // 请求USB设备访问权限 private void requestUsbPermission(UsbDevice device) { UsbManager usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE); usbManager.requestPermission(device, PendingIntent.getBroadcast(reactContext, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE)); } // 检查已连接的USB设备 private void checkForConnectedUsbDevices() { UsbManager usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE); HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); if (!deviceList.isEmpty()) { for (UsbDevice device : deviceList.values()) { if (device.getDeviceClass() == UsbDevice.USB_CLASS_MASS_STORAGE) { if (usbManager.hasPermission(device)) { sendDeviceConnectedEvent(device, true); findAndShareTargetFile(); } else { requestUsbPermission(device); } } else { sendDeviceConnectedEvent(device, false); } } } } // 查找目标文件并触发分享 private void findAndShareTargetFile() { // 获取USB存储挂载路径(Android 10+需适配Scoped Storage,这里以传统路径为例) File[] externalStorages = Environment.getExternalStorageDirectory().listFiles(); if (externalStorages != null) { for (File storage : externalStorages) { if (storage.isDirectory() && !storage.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { File targetFile = searchFile(storage, TARGET_FILE_NAME); if (targetFile != null) { shareFile(targetFile); sendFileFoundEvent(targetFile.getAbsolutePath()); return; } } } } sendFileNotFoundEvent(); } // 递归查找文件 private File searchFile(File directory, String fileName) { File[] files = directory.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { File result = searchFile(file, fileName); if (result != null) { return result; } } else { if (file.getName().toLowerCase(Locale.ROOT).equals(fileName.toLowerCase(Locale.ROOT))) { return file; } } } } return null; } // 调用系统分享功能 private void shareFile(File file) { if (!file.exists()) return; Uri fileUri = FileProvider.getUriForFile(reactContext, reactContext.getPackageName() + ".fileprovider", file); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Intent chooser = Intent.createChooser(shareIntent, "分享DataXYZ.txt文件"); chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); reactContext.startActivity(chooser); } // 发送设备连接事件到RN端 private void sendDeviceConnectedEvent(UsbDevice device, boolean hasPermission) { WritableMap deviceData = Arguments.createMap(); deviceData.putString("deviceName", device.getDeviceName()); deviceData.putInt("vendorId", device.getVendorId()); deviceData.putInt("productId", device.getProductId()); deviceData.putBoolean("hasPermission", hasPermission); deviceData.putBoolean("isStorageDevice", device.getDeviceClass() == UsbDevice.USB_CLASS_MASS_STORAGE); if (reactContext.hasActiveCatalystInstance()) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("UsbDeviceConnected", deviceData); } } // 发送设备断开事件到RN端 private void sendDeviceDisconnectedEvent(UsbDevice device) { WritableMap deviceData = Arguments.createMap(); deviceData.putString("deviceName", device.getDeviceName()); if (reactContext.hasActiveCatalystInstance()) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("UsbDeviceDisconnected", deviceData); } } // 发送文件找到事件到RN端 private void sendFileFoundEvent(String filePath) { WritableMap data = Arguments.createMap(); data.putString("filePath", filePath); if (reactContext.hasActiveCatalystInstance()) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("TargetFileFound", data); } } // 发送文件未找到事件到RN端 private void sendFileNotFoundEvent() { if (reactContext.hasActiveCatalystInstance()) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("TargetFileNotFound", null); } } // RN端可调用的手动查找方法 @ReactMethod public void triggerFileSearch() { findAndShareTargetFile(); } }
二、必要的配置
1. AndroidManifest.xml配置
添加USB权限和FileProvider配置:
<!-- USB权限 --> <uses-permission android:name="android.permission.USB_PERMISSION" /> <uses-feature android:name="android.hardware.usb.host" android:required="false" /> <!-- FileProvider配置(用于Android 7+文件分享) --> <application> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application>
2. 创建file_paths.xml文件
在res/xml目录下创建该文件,定义文件共享路径:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="." /> </paths>
三、React Native端调用示例
import { NativeEventEmitter, NativeModules } from 'react-native'; const { UsbDeviceListener } = NativeModules; const usbDeviceEmitter = new NativeEventEmitter(UsbDeviceListener); // 监听设备连接事件 usbDeviceEmitter.addListener('UsbDeviceConnected', (device) => { console.log('USB设备连接:', device); if (device.isStorageDevice && device.hasPermission) { console.log('开始查找目标文件'); // 可手动触发查找,也可等待原生自动触发 // UsbDeviceListener.triggerFileSearch(); } }); // 监听设备断开事件 usbDeviceEmitter.addListener('UsbDeviceDisconnected', (device) => { console.log('USB设备断开:', device); }); // 监听文件找到事件 usbDeviceEmitter.addListener('TargetFileFound', (data) => { console.log('找到目标文件:', data.filePath); }); // 监听文件未找到事件 usbDeviceEmitter.addListener('TargetFileNotFound', () => { console.log('未找到目标文件DataXYZ.txt'); });
四、关键说明
- 权限处理:通过
UsbManager.requestPermission请求USB设备访问权限,确保能读取USB存储内容 - 存储设备识别:通过
device.getDeviceClass() == UsbDevice.USB_CLASS_MASS_STORAGE判断是否为存储类设备 - 文件查找:递归遍历USB存储目录,忽略大小写查找
DataXYZ.txt - 文件分享:使用
FileProvider生成安全的文件URI,调用系统分享Intent完成分享 - Android版本适配:Android 10+需注意Scoped Storage限制,若遇到路径访问问题,需适配MediaStore或使用MANAGE_EXTERNAL_STORAGE权限
内容的提问来源于stack exchange,提问作者Yuriy Proniagin
相关产品推荐
相关产品推荐

